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

Multiplayer sucks

This commit is contained in:
John 2021-11-18 09:00:15 -06:00
parent a0f4e42914
commit 919576638c
7 changed files with 209 additions and 74 deletions

View File

@ -20,6 +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"
[display] [display]

View File

@ -7,11 +7,9 @@ func _ready():
# Signal to pass the fire location back to yet-unknown nodes # Signal to pass the fire location back to yet-unknown nodes
signal fire_at signal fire_at
func _on_Fire_pressed(): func _on_Fire_pressed():
var crosshair = get_node("Crosshair") var crosshair = get_node("Crosshair")
# hides crosshair # Check if the crosshair is in a valid position
crosshair.visible = false
if crosshair.validate_position(crosshair.position) == true: if crosshair.validate_position(crosshair.position) == true:
var crosshair_pos = crosshair.world_to_board_space(crosshair.position) var crosshair_pos = crosshair.world_to_board_space(crosshair.position)
# fires at position # fires at position

View File

@ -1,4 +1,4 @@
extends Node extends Control
var light_theme = load("res://light_theme.tres") var light_theme = load("res://light_theme.tres")
var dark_theme = load("res://dark_theme.tres") var dark_theme = load("res://dark_theme.tres")
@ -11,15 +11,16 @@ onready 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 = []
# turn counter # turn counter
var turn = 0 var turn = 0
# Variable transporting hit state between players # winner
var hit = false var winner = 0
# Variable tracking whether a game is multiplayer (so that the correct Player type can be spawned)
# TODO: Multiplayer
var is_multiplayer = false
# Every game is a multiplayer game, even the ones that aren't.
# We're taking the Minecraft approach, baby
var network_id
# 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():
@ -29,28 +30,70 @@ func _ready():
get_node("ConfirmationDialog").get_ok().rect_min_size.x = 100 get_node("ConfirmationDialog").get_ok().rect_min_size.x = 100
get_node("ConfirmationDialog").get_cancel().rect_min_size.x = 100 get_node("ConfirmationDialog").get_cancel().rect_min_size.x = 100
var _errno = 0; if multiplayer:
_errno += OptionsController.connect("change_theme", self, "_on_change_theme") # TODO: Spawn a lobby where people can either connect to a peer or create a server
_on_change_theme(OptionsController.get_theme()) pass
game_start()
func game_setup(): game_setup()
print_debug("Congrats! Setup complete.")
# Function used to keep track of which players are ready
remote func player_ready(pid):
print (get_tree().is_network_server())
var who = pid
# Here are some checks you can do, for example
assert(get_tree().is_network_server())
assert(who in Network.peer_info) # Exists
assert(not who in players_ready) # Was not added yet
players_ready.append(who)
if players_ready.size() == Network.peer_info.size():
rpc("game_start")
# Member functions: # Member functions:
# game_start: starts the game # game_start: starts the game
func game_start(): func game_setup():
# Create a player 1 # If there's no server connected, create one
var player = Player.instance() if not Network.connected:
# TODO: Create valid callback for player_ready # TODO: Create a fake peer who we can automate, for single-player mode
# It shouldn't connect to game_setup Network.start_server()
player.connect("player_ready", self, "game_setup") network_id = Network.get_network_id()
# Add player to scene tree # Create players for every player in Network.peer_info
add_child(player) for k in Network.peer_info.keys():
# Add player to players # Create a new player
players.append(player) var player = Player.instance()
# Set the player's opponent, for now
# Give the player a recognizable name, like "1", instead of "@@97"
player.name = str(k)
# The player controls themselves
player.set_network_master(k)
# Add the player to the list of players
players[k] = player
# Add the player to the scene tree
add_child(player)
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()
func game_start():
# Make sure we're the server
assert(get_tree().is_network_server())
while not winner:
for id in players.keys():
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(pid):
print ("_on_player_ready")
match pid:
1: player_ready(pid)
_: rpc("player_ready", pid)
# victory_screen: display the victory screen # victory_screen: display the victory screen
func victory_screen(): func victory_screen():
# TODO: Create the victory screen, fill it with knowledge # TODO: Create the victory screen, fill it with knowledge
@ -77,8 +120,3 @@ func _on_Button_button_down():
func _on_ConfirmationDialog_confirmed(): func _on_ConfirmationDialog_confirmed():
end() end()
func _on_change_theme(theme):
if theme == "light":
get_node("Buttons").set_theme(light_theme)
elif theme == "dark":
get_node("Buttons").set_theme(dark_theme)

View File

@ -1,38 +1,46 @@
extends Node extends Node
# Path to Board class, for instantiating new Boards in code # Emitted when the player is ready
var Board = preload("res://scenes/Game/Board.tscn")
# Preloaded assets, to be used later
# TODO: Move Setup into the Player. It's just here, for now, so that it can be tested and the game doesn't appear broken
var Setup = preload("res://scenes/Game/Setup.tscn")
# TODO: Move Fire into the Player. See above.
var Fire = preload("res://scenes/Game/Fire.tscn")
signal player_ready signal player_ready
# Player ID of this player # Preloaded assets, to be used later
var pid # Path to Board class, for instantiating new Boards in code
# board (an instance of the Board class) var Board = preload("res://scenes/Game/Board.tscn")
onready var board = Board.instance() # Path to Setup menu, so the player may set up their Board
var Setup = preload("res://scenes/Game/Setup.tscn")
# Path to Fire menu, so the player may fire on the opponent
var Fire = preload("res://scenes/Game/Fire.tscn")
var pid # Player ID
var board # Board
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
pid = int(name)
board = Board.instance()
remote 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)
# Member functions: # Member functions:
# hit: Called when opponent fires on us. # hit: Called when opponent fires on us.
# Update internal state, and return bool hit/miss, hit = true, miss = false # Update internal state, and return hit/miss/sunk
func hit(pos): remote func hit(pos):
var res = board.hit(pos) var res = board.hit(pos)
if res == -1: return res
return true
else: # mark: Called when the opponent returns hit/miss/sunk
return false # Update internal state, return ack/nak
pass remote func mark(pos, value):
# Mark the position on the top board
board.fire(pos, value)
# place_ship: called when ships are placed. # place_ship: called when ships are placed.
# forwards Ship locations to the Board, so that it may construct a ship # forwards Ship locations to the Board, so that it may construct a ship
@ -40,27 +48,32 @@ func hit(pos):
func place_ship(pos, size, orientation, variant): func place_ship(pos, size, orientation, variant):
board.place_ship(pos, size, orientation, variant) board.place_ship(pos, size, orientation, variant)
# setUp: set up the board given the placed ship locations # setup: set up the board given the placed ship locations
# translates the ship positions in the Setup UI to board-space, then places each ship # Places each ship onto the board
# ships: a list of lists of ship properties [[position, orientation, size, variant], ...] # ships: a list of lists of ship properties [[position, orientation, size, variant], ...]
func set_up(ships): func set_up(ships):
# Place all the ships # Place all the ships
for i in ships: for i in ships:
place_ship(i[0], i[1], i[2], i[3]) place_ship(i[0], i[1], i[2], i[3])
emit_signal("player_ready")
# Add the board to the tree # Add the board to the tree
add_child(board) add_child(board)
emit_signal("player_ready", pid)
# turnStart: 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]
func turnStart(): remote func turn_start():
# TODO: Yielf until Fire return print("turn_start")
add_child(Fire.instance()) var fire = Fire.instance()
var player_id = 0
var target = Vector2(0,0) add_child(fire)
return [player_id, target] yield(fire, "fire_at")
pass while not fire_at_position:
pass
var player_id = opponent_pid
var target = fire_at_position
fire_at_position = null
return {"id": player_id, "target": target}
# getBoard: returns the player's board # getBoard: returns the player's board
# returns: board # returns: board
@ -69,18 +82,17 @@ func getBoard():
# forfeit: ends game for player # forfeit: ends game for player
# Sinks all ships # Sinks all ships
# hits every single board tile # Ensures there are no ships left behind
func forfeit(): func forfeit():
for i in 10: for i in 10:
for j in 10: for j in 10:
var pos # Hit the board
pos.x = i hit(Vector2(i, j))
pos.y = j
hit(pos)
# getShipCount: get the number of ships the player has left alive # getShipCount: get the number of ships the player has left alive
func getShipCount(): func getShipCount():
var count = board.get_ship_count() return board.get_ship_count()
return count
pass
func _on_fire_at(pos):
fire_at_position = pos

View File

@ -33,7 +33,6 @@ func _on_Confirm_Placement_pressed():
# if this is more than zero, the ship is invalid # if this is more than zero, the ship is invalid
if get_node(ship).validate_placement(): if get_node(ship).validate_placement():
valid = false valid = false
print ("Placement: ", valid)
if valid == false: if valid == false:
get_node("PlaceShipDialog").popup() get_node("PlaceShipDialog").popup()
else: else:
@ -43,9 +42,6 @@ func _on_Confirm_Placement_pressed():
ship = get_node(ship) ship = get_node(ship)
var data = ship.get_shipdata() var data = ship.get_shipdata()
ship_data.append(data) ship_data.append(data)
#print out the array for testing
for x in ship_data:
print_debug("Ship Position: ", x[0], ", Ship Length: ", x[1], ", Ship Orientation: ", x[2], ", Variant: ", x[3])
# Return the shipLocation array to those listening on game_ready # Return the shipLocation array to those listening on game_ready
emit_signal("board_ready", ship_data) emit_signal("board_ready", ship_data)
queue_free() queue_free()

View File

@ -0,0 +1,85 @@
extends Node
const DEFAULT_PORT = 35879
const LOCALHOST = "127.0.0.1"
# Store peer info in a dictionary, by player ID
var peer_info = {}
# Store this player's hostname
var local_info = {"hostname": ""}
var connected = false
# Network -- handles server and client setup, and facilitates communication between the two
# start_server: Host the game
# port: TCP port
# max_players: Largest number of players allowed to connect at a time
func start_server(port = DEFAULT_PORT, max_players = 2):
get_hostname()
peer_info[1] = local_info
var peer = NetworkedMultiplayerENet.new()
peer.create_server(port, max_players)
get_tree().network_peer = peer
connected = true
return
func connect_server(ip = LOCALHOST, port = DEFAULT_PORT):
get_hostname()
var peer = NetworkedMultiplayerENet.new()
peer.create_client(ip, port)
get_tree().network_peer = peer
return
func disconnect_server():
get_tree().network_peer = null
connected = false
func get_hostname():
if local_info["hostname"] == "":
var hostname = []
var _ret = OS.execute("hostname", [], true, hostname)
local_info["hostname"] = hostname[0].split("\n")[0]
return local_info["hostname"]
func get_network_id():
return get_tree().get_network_unique_id()
func get_ip():
print(IP.resolve_hostname(get_hostname(), IP.TYPE_IPV4))
pass
func _ready():
var _trash
_trash = get_tree().connect("network_peer_connected", self, "_peer_connected")
_trash = get_tree().connect("network_peer_disconnected", self, "_peer_disconnected")
_trash = get_tree().connect("connected_to_server", self, "_server_connected")
_trash = get_tree().connect("connection_failed", self, "_connection_fail")
_trash = get_tree().connect("server_disconnected", self, "_server_disconnected")
func _peer_connected(id):
rpc_id(id, "register_peer", local_info)
pass
func _peer_disconnected(id):
peer_info.erase(id)
pass
func _server_connected():
# On connection to the server, you get a global network id
# Save your info at this id
peer_info[get_network_id()] = local_info
connected = true
pass
func _server_disconnected():
connected = false
pass
func _connection_fail():
connected = false
pass
remote func register_peer(info):
# Save player information under the sender id's peer info
peer_info[get_tree().get_rpc_sender_id()] = info
pass

View File

@ -0,0 +1,5 @@
extends Node
func _ready():
pass