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:
parent
a0f4e42914
commit
919576638c
@ -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]
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
if not Network.connected:
|
||||||
|
# TODO: Create a fake peer who we can automate, for single-player mode
|
||||||
|
Network.start_server()
|
||||||
|
network_id = Network.get_network_id()
|
||||||
|
# Create players for every player in Network.peer_info
|
||||||
|
for k in Network.peer_info.keys():
|
||||||
|
# Create a new player
|
||||||
var player = Player.instance()
|
var player = Player.instance()
|
||||||
# TODO: Create valid callback for player_ready
|
# Set the player's opponent, for now
|
||||||
# It shouldn't connect to game_setup
|
# Give the player a recognizable name, like "1", instead of "@@97"
|
||||||
player.connect("player_ready", self, "game_setup")
|
player.name = str(k)
|
||||||
# Add player to scene tree
|
# 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)
|
add_child(player)
|
||||||
# Add player to players
|
|
||||||
players.append(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)
|
|
||||||
|
@ -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")
|
||||||
|
while not fire_at_position:
|
||||||
pass
|
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
|
||||||
|
@ -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()
|
||||||
|
85
godot_ship/script/network/Network.gd
Normal file
85
godot_ship/script/network/Network.gd
Normal 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
|
5
godot_ship/script/options/Network.gd
Normal file
5
godot_ship/script/options/Network.gd
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
pass
|
Loading…
Reference in New Issue
Block a user