1
0
mirror of https://github.com/JohnBreaux/Boat-Battle.git synced 2024-11-15 05:25:57 +00:00

Multiplayer: Implement message passing system

This commit is contained in:
John 2021-11-22 22:04:21 -06:00
parent bc1e31ac8c
commit 5ed44e6deb
4 changed files with 82 additions and 58 deletions

View File

@ -20,7 +20,7 @@ MessageBus="*res://script/Message Bus.gd"
AudioBus="*res://script/audio controller/Audio Bus.gd" AudioBus="*res://script/audio controller/Audio Bus.gd"
OptionsController="*res://script/options/OptionsController.gd" OptionsController="*res://script/options/OptionsController.gd"
AudioController="*res://scenes/AudioController.tscn" AudioController="*res://scenes/AudioController.tscn"
Network="*res://script/network/Network.gd" Net="*res://script/network/Net.gd"
[display] [display]
@ -31,6 +31,10 @@ window/per_pixel_transparency/allowed=true
window/stretch/mode="viewport" window/stretch/mode="viewport"
window/stretch/aspect="keep" window/stretch/aspect="keep"
[global]
verbose=false
[input] [input]
ui_left={ ui_left={

View File

@ -5,13 +5,13 @@ var dark_theme = load("res://dark_theme.tres")
# Path to Player class, for instantiating new Players in code # Path to Player class, for instantiating new Players in code
onready var Player = preload("res://scenes/Game/Player.tscn") var Player = preload("res://scenes/Game/Player.tscn")
onready var Victory = preload("res://scenes/Game/Victory.tscn") var Victory = preload("res://scenes/Game/Victory.tscn")
# Array of instances of the Player class; stores the Players # Array of instances of the Player class; stores the Players
var players = {} # = player1, player2, ... var players = [] # = player1, player2, ...
var players_ready = [] var players_ready = []
# turn counter # turn counter
var turn = 0 var turn = 0
@ -37,56 +37,40 @@ func _ready():
game_setup() game_setup()
# Function used to keep track of which players are ready # Function used to keep track of which players are ready
mastersync func player_ready(): func player_ready():
var who = get_tree().get_rpc_sender_id() var who = get_tree().get_rpc_sender_id()
if get_tree().is_network_server() and who in Network.peer_info and not who in players_ready: if get_tree().is_network_server() and who in Net.peer_info and not who in players_ready:
print ("ASSERT SUCCESS") print ("ASSERT SUCCESS")
players_ready.append(who) players_ready.append(who)
if players_ready.size() == Network.peer_info.size(): if players_ready.size() == Net.peer_info.size():
rpc("game_start") rpc("game_start")
# Member functions: # Member functions:
# game_start: starts the game # game_start: starts the game
func game_setup(): sync func game_setup():
# If there's no server connected, create one # If there's no server connected, create one
if not Network.connected: if not Net.connected:
# TODO: Create a fake peer who we can automate, for single-player mode # TODO: Create a fake peer who we can automate, for single-player mode
Network.start_server() Net.start_host()
network_id = Network.get_network_id() network_id = Net.get_network_id()
var count = 0 # Create players for every player in Net.peer_info
# Create players for every player in Network.peer_info for k in Net.peer_info.keys():
for k in Network.peer_info.keys():
# Create a new player # Create a new player
var player = Player.instance() var player = Player.instance()
# Set the player's opponent, for now
player.opponent_pid = Network.peer_info.keys()[1 - count]
# Give the player a recognizable name, like "1", instead of "@@97" # Give the player a recognizable name, like "1", instead of "@@97"
player.name = str(k) player.name = str(k)
# The player controls themselves # The player controls themselves
player.set_network_master(k) player.set_network_master(k)
# Add the player to the list of players # Add the player to the list of players
players[k] = player players.append(player)
# Add the player to the scene tree # Add the player to the scene tree
add_child(player) add_child(player)
count += 1
pass pass
# Connect to your own player_ready signal
players[network_id].connect("player_ready", self, "_on_player_ready")
# Have your player set up the board:
players[network_id].set_up_begin()
mastersync func game_start(): func game_start():
# Make sure we're the server # Make sure we're the server
assert(get_tree().is_network_server()) pass
while not winner:
for id in players.keys():
# TODO: RPC always returns nothing.
# Figure out how to work around this.
var hit = players[id].rpc_id(id, "turn_start")
var result = players[hit["id"]].rpc_id(hit["id"], "hit", hit["target"])
players[id].rpc_id(id, "mark", hit["target"], result)
pass
func _on_player_ready(): func _on_player_ready():
print ("_on_player_ready") print ("_on_player_ready")

View File

@ -3,7 +3,6 @@ extends Node
# Emitted when the player is ready # Emitted when the player is ready
signal player_ready signal player_ready
# Preloaded assets, to be used later # Preloaded assets, to be used later
# Path to Board class, for instantiating new Boards in code # Path to Board class, for instantiating new Boards in code
var Board = preload("res://scenes/Game/Board.tscn") var Board = preload("res://scenes/Game/Board.tscn")
@ -12,33 +11,33 @@ var Setup = preload("res://scenes/Game/Setup.tscn")
# Path to Fire menu, so the player may fire on the opponent # Path to Fire menu, so the player may fire on the opponent
var Fire = preload("res://scenes/Game/Fire.tscn") var Fire = preload("res://scenes/Game/Fire.tscn")
var pid # Player ID # Members
var pid # Player ID
var board # Board var board # Board
var fire_at_position # Position to fire at var fire_at_position # Position to fire at
var opponent_pid # PID of opponent
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
func _ready(): func _ready():
# Set the player ID according to which network peer ID we are # Set the player ID according to which network peer ID we are
pid = int(name) pid = int(name)
board = Board.instance()
mastersync func set_up_begin(): func set_up_begin():
var setup = Setup.instance() var setup = Setup.instance()
setup.connect("board_ready", self, "set_up") setup.connect("board_ready", self, "set_up")
add_child(setup) add_child(setup)
board = Board.instance()
# Member functions: # Member functions:
# hit: Called when opponent fires on us. # hit: Called when opponent fires on us.
# Update internal state, and return hit/miss/sunk # Update internal state, and return hit/miss/sunk
mastersync func hit(pos): func hit(pos):
var res = board.hit(pos) var res = board.hit(pos)
return res return res
# mark: Called when the opponent returns hit/miss/sunk # mark: Called when the opponent returns hit/miss/sunk
# Update internal state, return ack/nak # Update internal state, return ack/nak
mastersync func mark(pos, value): func mark(pos, value):
# Mark the position on the top board # Mark the position on the top board
board.fire(pos, value) board.fire(pos, value)
@ -62,7 +61,7 @@ func set_up(ships):
# turn_start: start player's turn # turn_start: start player's turn
# Initiates the player's turn, and blocks until the player selects a location to fire upon # Initiates the player's turn, and blocks until the player selects a location to fire upon
# returns: fire = [player id, target coordinates] # returns: fire = [player id, target coordinates]
mastersync func turn_start(): func turn_start():
print("turn_start") print("turn_start")
var fire = Fire.instance() var fire = Fire.instance()
@ -70,10 +69,9 @@ mastersync func turn_start():
yield(fire, "fire_at") yield(fire, "fire_at")
while not fire_at_position: while not fire_at_position:
pass pass
var player_id = opponent_pid
var target = fire_at_position var target = fire_at_position
fire_at_position = null fire_at_position = null
return {"id": player_id, "target": target} return target
# getBoard: returns the player's board # getBoard: returns the player's board
# returns: board # returns: board

View File

@ -3,36 +3,72 @@ extends Node
const DEFAULT_PORT = 35879 const DEFAULT_PORT = 35879
const LOCALHOST = "127.0.0.1" const LOCALHOST = "127.0.0.1"
signal incoming
class Mail:
var from
var message
var type
func _init(f, m, t):
from = f; message = m; type = t
enum {REPLY, REQUEST}
# Store peer info in a dictionary, by player ID # Store peer info in a dictionary, by player ID
var peer_info = {} var peer_info = {}
# Store this player's hostname # Store this player's hostname
var local_info = {"hostname": ""} var local_info = {"hostname": ""}
var connected = false var connected = false
var hosting = false
# FIFO queue of Mails
var inbox = []
# Network -- handles server and client setup, and facilitates communication between the two # Network -- handles server and client setup, and facilitates communication between the two
# receive: Called when an incoming message is received
# item: The message received from the sender
remote func receive(mail:Mail):
# Get the sender's ID and force letter to be properly addressed
mail.from = get_tree().get_rpc_sender_id()
print_debug(mail.from, mail.message, mail.type)
# Sent it off to anything that expects mail
emit_signal("incoming", mail)
func send(id, mail:Mail):
# Make the recipient receive the mail
rpc_id(id, "receive", mail)
# start_server: Host the game # start_server: Host the game
# port: TCP port # port: TCP port
# max_players: Largest number of players allowed to connect at a time # max_players: Largest number of players allowed to connect at a time
func start_server(port = DEFAULT_PORT, max_players = 2): func start_host(port = DEFAULT_PORT, max_players = 2):
get_hostname() get_hostname()
peer_info[1] = local_info peer_info[1] = local_info
var peer = NetworkedMultiplayerENet.new() var peer = NetworkedMultiplayerENet.new()
peer.create_server(port, max_players) peer.create_server(port, max_players)
get_tree().network_peer = peer get_tree().network_peer = peer
connected = true connected = true
hosting = true
return return
func connect_server(ip = LOCALHOST, port = DEFAULT_PORT): # connect_server: Connect to a host
func connect_host(ip = LOCALHOST, port = DEFAULT_PORT):
get_hostname() get_hostname()
var peer = NetworkedMultiplayerENet.new() var peer = NetworkedMultiplayerENet.new()
peer.create_client(ip, port) peer.create_client(ip, port)
get_tree().network_peer = peer get_tree().network_peer = peer
return
func disconnect_server(): func disconnect_host():
get_tree().network_peer = null # Set state to disconnected
connected = false connected = false
hosting = false
# Attempt disconnection
if get_tree().network_peer:
get_tree().network_peer.close_connection()
# Disconnect
get_tree().network_peer = null
# Clear peer info
peer_info = {}
func get_hostname(): func get_hostname():
if local_info["hostname"] == "": if local_info["hostname"] == "":
@ -50,11 +86,12 @@ func get_ip():
func _ready(): func _ready():
var _trash var _trash
_trash = get_tree().connect("network_peer_connected", self, "_peer_connected") _trash = get_tree().connect("network_peer_connected", self, "_peer_connected" )
_trash = get_tree().connect("network_peer_disconnected", self, "_peer_disconnected") _trash = get_tree().connect("network_peer_disconnected", self, "_peer_disconnected")
_trash = get_tree().connect("connected_to_server", self, "_server_connected") _trash = get_tree().connect("connected_to_server", self, "_host_connected" )
_trash = get_tree().connect("connection_failed", self, "_connection_fail") _trash = get_tree().connect("server_disconnected", self, "_host_disconnected")
_trash = get_tree().connect("server_disconnected", self, "_server_disconnected") _trash = get_tree().connect("connection_failed", self, "_connection_fail" )
func _peer_connected(id): func _peer_connected(id):
rpc_id(id, "register_peer", local_info) rpc_id(id, "register_peer", local_info)
@ -64,20 +101,21 @@ func _peer_disconnected(id):
peer_info.erase(id) peer_info.erase(id)
pass pass
func _server_connected():
func _host_connected():
# On connection to the server, you get a global network id # On connection to the server, you get a global network id
# Save your info at this id # Save your info at this id
peer_info[get_network_id()] = local_info peer_info[get_network_id()] = local_info
# Set state to connected
connected = true connected = true
pass
func _server_disconnected(): func _host_disconnected():
connected = false # Ensure host is disconnected
pass disconnect_host()
func _connection_fail(): func _connection_fail():
connected = false # Ensure Net state is clear
pass disconnect_host()
remote func register_peer(info): remote func register_peer(info):
# Save player information under the sender id's peer info # Save player information under the sender id's peer info