mirror of
https://github.com/JohnBreaux/Boat-Battle.git
synced 2024-11-14 21:15:58 +00:00
Multiplayer: Implement message passing system
This commit is contained in:
parent
bc1e31ac8c
commit
5ed44e6deb
@ -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={
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user