Hour 23. Game 3: Networked Bomberman Clone
    What You’ll Learn in This Hour:
    Handle networking in a real game
    Use the TileMap and TileSet tools to create a map
    Combine the Sprite and AnimationPlayer for your animations
    Use AutoLoad to make a script global
    In this hour, we will create our final game. It is a clone of the Nintendo Entertainment System-era legendary game Bomberman, but with a killer
    feature the original didn’t have in its time: networked multiplayer. The game consists of multiple players evolving in a top-down labyrinth with the
    ability to plant bombs to break walls and kill other players, increasing their scores. This game is pretty classic, and making a single-player
    version of it shouldn’t be too much trouble after what we’ve learned from games 1 and 2, so it will allow us to focus more on the networking
    aspects.
    Concept and Design
    Each player is controlled by its own instance of the game. Ideally, each instance is on its own computer with a real human player. But if you lack
    spare computers and/or friends with whom share your joy of Godot, it’s perfectly fine to start the game multiple times on your own computer
    and switch from one window to another to control each player in turn.
    As said earlier, players evolve in a tiled-based labyrinth, so they can move vertically and horizontally with the arrow keys and plant bombs with
    the space bar. Once planted, a bomb will wait two seconds before blowing up. Given it’s a tiled-based game, the bomb detonates following
    vertical and horizontal tiles (it doesn’t propagate in diagonal tiles).
    The detonation propagates until it has reached two tiles or hits a solid wall. On the other hand, softer walls are destroyed by the explosion, and
    players are killed.
    When killed, a player returns to its beginning position, and its killer gets 100 more points (unless the player committed suicide, thus getting −50
    points)
    FIGURE 23.1
    Screenshot of the final game.
    Given that it’s a multiplayer game, we need to build a main menu to choose whether we want to start the game as a server or to join an existing
    game as a client. Finally, we need a lobby in which players wait once they join until the server decides it’s time to start the game.
    Bootstrap the Project
    You know the drill: create a new project and add the classic folders (scene, scripts, etc.) to have a nicely organized project.
    FIGURE 23.2
    Our project directory.
    Before we start jumping on the code, you should copy the fonts and Sprite resources from the Hour 23 folder. Of course, when reading this
    tutorial, don’t hesitate to have a look at the scripts or open this project with the Godot editor if you get stuck.
    Setting Up the Scenes
    As usual withGodot, we first divide our game into small scenes, allowing us to break down complexity and improve modularity:
    TABLE 23.1 Scenes Needed for the Game
    Scene
    Description
    Menu and
    Lobby
    Given how simple the menu and lobby scenes are, we can combine them into a single scene and choose which one to display (or
    none once the game is started).
    This scene consists of a
    Control node (this is the base node for UI) with two
    Control children (one for the menu, one for
    the lobby), each one composed of a mix of
    Label,
    Button, and
    Input.
    Arena
    This is our main scene where the instantiated player scenes evolve and the bomb and explosion scenes are created. We also have
    to handle the labyrinth here, which is the perfect use case for a
    TileMap node and the starting position for players with
    Position2D nodes. Finally, this scene is responsible for displaying the score for each player through a simple
    Label.
    Player
    The player scene consists of a
    KinematicBody2D with a
    CollisionShape2D, which allows us to easily move it and handle
    its collisions. We also need a
    Sprite and an
    AnimationPlayer to display the player on the screen and animate it when it is
    moving. Finally, a
    Timer node is used to avoid the player spamming bombs.
    111111111
    22222222Bomb
    Given that the bomb won’t move once planted, we can represent it with a
    StaticBody2D with a
    CollisionShape2D. Just
    like for the player, we use
    Sprite and
    AnimationPlayer nodes to help players spot when a bomb is about to blow up.
    Speaking of this, a
    Timer allows us to configure when the explosion occurs.
    ExplosionBecause an explosion propagates by following the tiles, we can rely on the arena’s tilemap to handle collisions. This means our
    explosion scene has to display a per-tile explosion animation using a
    Sprite and an
    AnimationPlayer.
    As we saw in Hour 21, one of the big advantages of the Godot high-level network system is that it allows us to first focus on developing a
    single-player game, then add multiplayer capability to it by declaring how nodes should synchronize together. To follow this idea, we’ll start by
    making a single-player Bomberman _game, then we’ll switch our focus to making it multiplayer.
    Making the Scenes
    Let’s look at how to make the scenes.
    Creating Player, Bomb, and Explosion Scenes
    Start with the most important scene: the player. Often, it is one of the most complex scenes of the game. However, our game is still small, so
    there is no reason to panic. Hour 9 told us that for a 2D platform game,
    KinematicBody2D is the best root node for player-controlled
    scenes, given that it isn’t affected by physics while it still allows us to detect a collision and move it accordingly if needed.
    As you already know, a
    KinematicBody2D needs a collision shape to work, so we’ll add a
    CollisionShape2D and configure its
    Shape property. Given that our player will move on tiles, we may be tempted to use a rectangular shape the size of a tile. However, this causes
    continuous collisions that often end up with our node becoming stuck. So, it’s a much better solution to use a circle collision shape that’s
    slightly smaller than the size of a tile.
    Speaking of which, we should choose the tile size of 32 pixels, so we will scale all of our Sprites and collision shapes to this size.
    We will also add as a child to the root node: a
    Timer node to control the rate of fire of the player. We’ll set its Wait Time to one second,
    and, given that this timer is triggered by code when a bomb is planted, select the One Shot property and make sure Autostart is disabled.
    NOTE
    Collision Shape and Scaling
    A reminder from Hour 9: be careful when scaling collision shapes. Make sure they are uniform and have nonnegative values. Otherwise,
    strange things will happen with your physics.
    It’s time to make the player visible. We could use the simple
    AnimatedSprite for this task, but to add a bit of variety, we’ll choose the
    more powerful
    Sprite and
    AnimationPlayer combo this time. We aren’t in Kansas anymore.
    First, add the
    Sprite node as a child of the root, then configure it using the Texture property. Click on load and select
    “sprites/player1.png.” At this point, you should realize that the image we loaded is not a single Sprite but a spritesheet (an image containing
    one next to other, multiple Sprites), so configure the
    Sprite accordingly with the Animation:Vframes and Animation:Hframes properties
    (in our case, the spritesheet contains one row and three columns, so Vframes = 1 and Hframes = 3).
    Now we can use the Frame property to pass through the animations and choose the default one. As you can see, our spritesheet is composed
    of three Sprites: an idle pose and two steps that compose a primitive walking animation (Figure 23.3).
    FIGURE 23.3
    Sprite configuration on the player node.
    Using the Frame property by hand (or even scripting its update withGDNative) is a no-go when you can use a shiny tool like the
    AnimationPlayer. Add this node (make sure it is a direct child of the root) and start configuring its animations.
    Click on the
    AnimationPlayer, and the animation menu will open on the lower part of the editor. Click on
    to create a new animation
    and call it idle. Now use the Scene Tree viewer to select the Sprite node. In the Inspector, find the Animation:Frame property and make sure
    its value is 0. Now click on
    , a popup that asks you if you want to add a new track to your animation. Click “create.”
    FIGURE 23.4
    Idle animation for the player.
    You have created your first animation composed of . . . one frame. This seems silly putting it this way, but this means you can now easily
    improve the one-frame idle animation. Once the walking animation is triggered, you’ll need to trigger this simple idle animation, or the player
    will walk forever.
    Speaking of the walking animation, it is the same thing as idle animation, except you click two times on the
    button next to the
    Animation:Frame property (once per frame). This time, be careful to configure the Length and Step property of your animation; otherwise,
    you will end up with something clunky (typically frame 1 at 0 seconds, frame 2 at 0.1 seconds, then frame 1 again 0.9 seconds later when the
    111111111
    22222222animation loop’s given default Length is 1 second). Also, don’t forget to set the track to
    Discrete; otherwise, the default continuous mode
    blends your two frames together and you’ll end up with a single frame playing continuously (Figure 23.5).
    FIGURE 23.5
    Walking animation for the player.
    Finally, you should have a third and last animation to do the respawn on. Do the same steps, but this time, don’t change the Sprite, but make it
    blink (it will help the player spot where his character has respawned). To do so, use the
    next to the Visibility:Visible property. Note that
    this is where using an
    AnimatedSprite for our animation would fall short (Figure 23.6).
    FIGURE 23.6
    Respawn animation for the player.
    TRY IT YOURSELF
    Creating the Bomb Scene
    The bomb scene is pretty similar to the player scene:
    1. Create a
    StaticBody2D node as root and rename it “Bomb.”
    2. Add a child
    CollisionShape2D and activate its Disabled property. The idea is that a player plants a bomb on the tile on
    which it currently stands, so you’ll want to wait a bit before enabling the bomb physics to let the player leave the tile and avoid
    glitches.
    3. Add a
    Timer, rename it “EnableCollisionTimer,” and configure it with Wait Time = 0.5, One Shot = True, and Auto Start =
    True.
    4. Create a
    Sprite and set the Texture to the “sprites/bomb.png.” Like for the player, it is a 3 × 1 spritesheet that makes the
    bomb get redder the closest it is to exploding.
    5. Finally, create and use
    AnimationPlayer, configure a default animation lasting 2 seconds, and change the bomb color at
    0, 1, and 1.5 seconds. Activate the Playback:Active chec
    k
    box to start the animation when the node gets created.
    Note that we don’t need to create a
    Timer to control when the bomb will blow: we will obtain similar results by connecting to the
    animation_finished event provided by our
    AnimationPlayer.
    TRY IT YOURSELF
    Creating the Explosion Scene
    The explosion scene is even simpler:
    1. Create a
    Sprite node as root, call it “Explosion,” and assign to it the “sprites/explosion.png” image. This time, the
    spritesheet is a 3 x 7 representing a seven-frame center and side and end explosion animations.
    2. Add an
    AnimationPlayer and create three animations: “explosion_center,” “explosion_side,” and “explosion_side_border,”
    each 0.35 seconds long with steps of 0.05 seconds.
    Creating the Arena Scene
    We start with a
    Node2D as root of the scene (don’t forget to rename it “Arena” for the sake of clarity). Strictly speaking, this node is not
    really useful, but it allows you to better divide the subnodes.
    To construct the labyrinth, you can create a scene for each type of block, then instantiate and place them by hand on the Arena scene.
    However, this is a really cumbersome task, and we have a much better tool to achieve this: the
    TileMap node. As it name indicates, this
    type of node allows you to easily select and place tiles on your scene.
    But before you can use a
    TileMap, you’ll need to build a
    Tileset that defines different tiles to be placed. For our game, we need three
    types of tiles:
    BackgroundBrick: represents the ground where the players will walk
    SolidBrick: represents the wall of the arena; the player cannot cross them, and explosions are stopped
    BreakableBrick: blocks players like SolidBrick; player is destroyed once hit by an explosion
    111111111
    22222222Create a new scene, call it “scenes/tileset-source.tscn,” and add a root
    Node2D. Now, we are going to add
    Sprite children nodes,
    with each of them representing a tile. Make sure to give a meaningful name for each of these nodes, as the tiles will keep them once imported
    into the
    TileMap.
    For each
    Sprite, load “sprites/bricks.png” in the Texture property (once again, it’s a 3 × 1 spritesheet, so correct Vframes and Hframes
    accordingly) and configure the Frame property so that each tile is different.
    On top of that, add a
    StaticBody2D with a
    CollisionShape2D (with a
    RectangularShape2D of 32 × 32) child to the SolidBrick
    and BreakableBrick (Figure 23.7).
    FIGURE 23.7
    TileSet source scene.
    Now open the menu Scene –> Convert To . . . –> TileSet and save the tileset as “scenes/tileset.res.”
    Back to the arena scene. Add a
    TileMap child node, configure the
    Tileset property to load the shiny new tileset, and start painting the
    map with tiles (Figure 23.8).
    FIGURE 23.8
    Drawing the tilemap.
    Once you’re happy with the results of your map, move on by adding spawn positions for the player. Add a
    Node2D and name it
    “SpawnPositions.” Inside it, add four
    Position2D where you want your players to start. (Of course, they should be placed on a
    BackgroundBrick, since you don’t want a player to get stuck in a wall.)
    Now, create another
    Node2D and name it “Players,” which is the node where you’ll register all player scenes to easily iterate through them
    (more on this in the scripting part).
    Finally, create a simple
    Label node named “ScoresBoard” that displays the score of each player.
    FIGURE 23.9
    Scene Tree of the player, bomb, explosion, and arena.
    NOTE
    Make the Draw Order Right
    Even in 2D, the Z axis is useful to determine what is displayed on top of what. Here, we want the z value for the arena scene to stay at the
    default value of 0, then set it higher for bomb, player, and explosion, in this order.
    Scripts and Input
    To control the player, first attach a script to the player scene root node, save it as “scripts/player.gd,” and start tweaking it (see Listing 23.1).
    LISTING23.1 Player Movements with Kinematic Body — player.gd
    Click here to view code image
    const WALK_SPEED = 200
    var dead = false
    var direction = Vector2()
    var current_animation = “idle”
    func _physics_process(delta):
    if dead:
    return
    if (Input.is_action_pressed(“ui_up”)):
    direction.y = - WALK_SPEED
    elif (Input.is_action_pressed(“ui_down”)):
    direction.y = WALK_SPEED
    else:
    direction.y = 0
    if (Input.is_action_pressed(“ui_left”)):
    direction.x = - WALK_SPEED
    elif (Input.is_action_pressed(“ui_right”)):
    direction.x = WALK_SPEED
    else:
    direction.x = 0
    move_and_slide(direction)
    111111111
    22222222rotation = atan2(direction.y, direction.x)
    var new_animation = “idle”
    if direction:
    new_animation = “walking”
    if new_animation != current_animation:
    animation.play(new_animation)
    current_animation = new_animation
    It shouldn’t be a surprise now: create a _physics_process() function to control the movement of the player. Use the move_and_slide()
    function provided by the
    KinematicBody2D, which automatically takes care of moving the player and handling collisions for you. Update
    the animation if needed (because re-setting the animation at each frame would make it only play the first frame of it).
    Continue by creating a _process function to allow the player to plant bombs (Listing 23.2).
    LISTING23.2 Player Planting Boms
    Click here to view code image
    var can_drop_bomb = true
    var tilemap = get_node(“/root/Arena/TileMap”)
    func _process(delta):
    if dead:
    return
    if Input.is_action_just_pressed(“ui_select”) and can_drop_bomb:
    dropbomb(tilemap.centered_world_pos(position))
    can_drop_bomb = false
    drop_bomb_cooldown.start()
    sync func dropbomb(pos):
    var bomb = bomb_scene.instance()
    bomb.position = pos
    bomb.owner = self
    get_node(“/root/Arena”).add_child(bomb)
    func _on_DropBombCooldown_timeout():
    can_drop_bomb = true
    Like its name suggests, connect _on_DropBombCooldown_timeout() to the player DropBombCooldown timer.
    Bomb and Explosion
    Start by creating “scripts/bomb.gd” and connect it to the bomb scene (see Listing 23.3). This script does three things: first, it enables the
    CollisionShape2D once the
    Timer has finished, then it waits for
    AnimationPlayer to end spawn explosion scene instances
    according to the type of tiles present on the
    TileMap, and last, but not least, it finishes by destroying itself (because it blew up,
    remember?).
    LISTING23.3 Bomb Explosion Expanding Algorithm
    Click here to view code image
    extends StaticBody2D
    const EXPLOSION_RADIUS = 2
    onready var explosion_scene = preload(“res://scenes/explosion.tscn”)
    onready var tilemap = get_node(“/root/Arena/TileMap”)
    onready var tile_solid_id = tilemap.tile_set.find_tile_by_name(“SolidBrick”)
    func propagate_explosion(centerpos, propagation):
    for i in range(1, EXPLOSION_RADIUS + 1):
    var tilepos = center_tile_pos + propagation i
    if tilemap.get_cellv(tilepos) != tile_solid_id:
    # Boom !
    var explosion = explosion_scene.instance()
    explosion.position = tilemap.centered_world_pos_from_tilepos(tilepos)
    get_parent().add_child(explosion)
    else:
    # Explosion was stopped by a solid block
    break
    func _on_AnimationPlayer_animation_finished( name ):

    # Propagate the explosion by starting where the bomb was and go
    # away from it in straight vertical and horizontal lines
    propagate_explosion(position, Vector2(0, 1))
    propagate_explosion(position, Vector2(0, -1))
    propagate_explosion(position, Vector2(1, 0))
    propagate_explosion(position, Vector2(-1, 0))

    111111111
    22222222The important point here is how you use
    TileMap’s map_to_world(), get_cellv() and find_tile_by_name() to retrieve the tiles to
    instantiate an explosion scene.
    Speaking of explosions, it’s time to create and attach its script “scripts/explosion.gd.” The idea is roughly the same as in bomb.gd: use
    TileMap to retrieve the tile in which the explosion takes place, making sure this tile is a BackgroundTile. Then retrieve the players’ nodes (now
    we are happy to have this “Arena/Players” node) and check for each to see if it is standing on the exploding tile (Listing 23.4).
    LISTING23.4 Explosions
    Click here to view code image
    extends Node2D
    onready var tilemap = get_node(“/root/Arena/TileMap”)
    onready var animation = get_node(“AnimationPlayer”)
    func _ready():
    # Retrieve the tile hit by the explosion and flatten it to the ground
    var tile_pos = tilemap.world_to_map(position)
    var tile_background_id = tilemap.tile_set.find_tile_by_name(“BackgroundBrick”)
    tilemap.set_cellv(tile_pos, tile_background_id)
    # Now that we know which tile is blowing up, retrieve the players
    # and destroy them if they are on it
    for player in get_tree().get_nodes_in_group(‘players’):
    var playerpos = tilemap.world_to_map(player.position)
    if playerpos == tile_pos:
    player.damage()
    Just like for the bomb, we should also add a function connected to the
    AnimationPlayer’s end of animation to free the Explosion node
    once it is no longer needed.
    TRY IT YOURSELF
    Single-player Bomberman
    With a bit of polish, your _Bomberman _game will be playable:
    1. Add a player scene to the Arena/Players node.
    2. Configure the arena scene as the main scene of your God
    o
    t project.
    *3.
    Hit run and start playing your _Bomberman
    .
    FIGURE 23.10
    Our game running in single-player mode.
    4. You can now add a second player to your scene and correct the player.gd script to handle different keys depending on which
    Player node is currently processed. Now you can have a real duel.
    Enter Multiplayer
    As you saw earlier, you cannot start the game right on the arena scene if your game is multiplayer: you have to let the player first choose if he
    wishes to host a game or join one. So, you should create the lobby scene, which cumulates the main menu with the lobby menu where users
    are listed. This is mainly the GUIwidget placing and signaling connection, and doesn’t have much to do with multiplayer, so we leave it to you
    (just copy/paste “scenes/lobby.tscn” and “scripts/lobby.gd” from the Hour 23 folder).
    FIGURE 23.11
    AutoLoad configuration for gamestate.gd.
    On top of that, we should keep information about the players (e.g., nicknames) before the Arena scene is created (so we cannot store that
    under “Arena/Players/”).
    One solution is to set the lobby as the main scene, then store players’ information in a script connected to the root node of this scene. This is
    doable; however, it’s more elegant to store that information in an AutoLoad and have them available in all scenes, even if you decide to
    remove the lobby scene (for example, if you start the game as a dedicated server).
    Create a new “scripts/gamestate.gd,” then select Project > Project Settings > AutoLoad, load the newly created script, and make sure
    Singleton is set to Enable (Figure 23.12).
    111111111
    22222222FIGURE 23.12
    AutoLoad configuration for gamestate.gd.
    Now, open the gamestate.gd script and copy the host_game() and join_game() functions from Listing 23.1. Those functions are called when
    the user clicks on the host or join buttons, along with hiding the main menu to show the list of players waiting for the game to start.
    As you saw in Hour 22, a player joining or leaving the game triggers a signal that connects to a function to keep the list of players up to date
    (Listing 23.5).
    LISTING23.5 Gamestate Network Signals Handling
    Click here to view code image
    var players = {}
    func _ready():
    get_tree().connect(“network_peer_connected”, self, “_player_connected”)
    get_tree().connect(“network_peer_disconnected”, self,”_player_disconnected”)
    get_tree().connect(“connected_to_server”, self, “_connected_ok”)
    get_tree().connect(“connection_failed”, self, “_connected_fail”)
    get_tree().connect(“server_disconnected”, self, “_server_disconnected”)
    func _player_disconnected(id):
    # Each peer get this notification when a peer diseapers,
    # so we remove the corresponding player data.
    var player_node = get_node(“/root/Arena/Players/%s” % id)
    if player_node:
    # If we have started the game yet, the player node won’t be present
    player_node.queue_free()
    players.erase(id)
    func _connected_ok():
    # This method is only called from the newly connected
    # client. Hence we register ourself to the server.
    var player_id = get_tree().get_network_unique_id()
    # Note given this call
    rpc(“register_player_to_server”, player_id, player_nickname)
    # Now just wait for the server to start the game
    emit_signal(“waiting_for_players”)
    func _connected_fail():
    _stop_game(“Cannot connect to server”)
    func _server_disconnected():
    _stop_game(“Server connection lost”)
    Here, the _stop_game() function disables the network and switches back to the main menu, but that is up to you.
    More interesting is the RPC call on register_player_to_server() function. The idea is to have the joining client call the server to communicate
    his nickname. In return, the server tells him if he can join the game (if the game is not yet started and if there are less than four players). It turn,
    the server calls the clients with RPC to notify them of the newly joined one and vice versa (Listing 23.6).
    LISTING23.6 Gamestate Register New Player
    Click here to view code image
    master func register_player_to_server(id, name):
    # As server, we notify here if the new client is allowed to join the game
    if game_started:
    rpc_id(id, “_kicked_by_server”, “Game already started”)
    elif len(players) == MAX_PLAYERS:
    rpc_id(id, “_kicked_by_server”, “Server is full”)
    # Send to the newcomer the already present players
    for p_id in players:
    rpc_id(id, “register_player”, p_id, players[p_id])
    # Now register the newcomer everywhere, note the newcomer’s peer will
    # also be called
    rpc(“register_player”, id, name)
    # register_player is slave, so rpc won’t call it on our peer
    # (of course we could have set it sync to avoid this)
    register_player(id, name)
    slave func register_player(id, name):
    players[id] = name
    Eventually, the user on the server will lose patience and hit the start game button. This sends an RPC to everybody (including himself) on the
    start_game() procedure (Listing 23.7).
    LISTING23.7 Gamestate Start Game
    Click here to view code image
    sync func start_game():
    111111111
    22222222# Load the main game scene
    var arena = load(“res://scenes/arena.tscn”).instance()
    get_tree().get_root().add_child(arena)
    var spawn_positions = arena.get_node(“SpawnPositions”).get_children()
    # Populate each player
    var i = 0
    for p_id in players:
    var player_node = player_scene.instance()
    player_node.set_name(str(p_id)) # Useful to retrieve the player node with
    a node path
    player_node.position = spawn_positions[i].position

    player_node.set_network_master(p_id)
    arena.get_node(“Players”).add_child(player_node)
    i += 1

    emit_signal(“game_started”)
    Here, we create the arena scene and add to it a player scene instance per peer connected. Be careful to configure each player scene instance
    with a different master corresponding to its peer; otherwise, only the server will be able to play.
    Synchronization in Player, Bomb, and Explosion
    If we now try to run the game in multiplayer, we can start a server, connect the client, and even create the Arena scene on each connected peer
    with all our on players on it.
    But the hard truth hits us as soon as we start sending inputs to our game: there is no synchronization between peers on the player, bomb, and
    explosion scenes. Now is the time to fix this.
    Concerning the player scenes, we already configured them to be owned by their respective peers. This means we can easily use a
    is_network_master() call to run the code responsible for processing inputs on the master peer only (Listing 23.8).
    LISTING23.8 Player Controlled Only by its Master Peer
    Click here to view code image
    func _physics_process(delta):
    if not is_network_master():
    return
    if not dead:
    if (Input.is_action_pressed(“ui_up”)):

    rpc(‘_dropbomb’, tilemap.centered_world_pos(position))

    func _process(delta):
    if not is_network_master() or dead:
    return
    if Input.is_action_just_pressed(“ui_select”) and can_drop_bomb:

    move_and_slide(direction)
    _update_rot_and_animation(direction)
    # Send to other peers our player info
    # Note we use unreliable mode given we synchronize during every frame
    # so losing a packet is not an issue
    rpc_unreliable(“_update_pos”, position, direction)
    sync func _dropbomb(pos):
    var bomb = bomb_scene.instance()
    bomb.position = pos
    bomb.owner = self
    get_node(“/root/Arena”).add_child(bomb)
    slave func _update_pos(new_pos, new_direction):
    position = new_pos
    direction = new_direction
    _update_rot_and_animation(direction)
    Now the trick is to separate the code responsible for handling inputs from the one that actually updates the node’s state (this is, what we do for
    dropbomb()). Note that sometimes it’s easier to create a function specially dedicated to the slave synchronization: here, we work directly on
    the master position and direction properties, then use _update_pos() on the slaves. This has two advantages: first, it avoids creating
    temporary values to store the new direction and position before calling the RPC, then it allows us to use rpc_unreliable to call the slaves (the
    call is done on each frame, so we don’t care if we lose synchronization from time to time), given that the master always keeps the real value of
    those properties.
    What about the bomb and explosion scenes, you may ask? Well, good news first: our bomb scene is totally deterministic across the peers, so
    we don’t have to do anything for it. Regarding the explosion scene, given that it is created by the bomb, you can be sure it will spawn in the right
    place and blow at the right time. The trick is that, given how the players are synchronized with an unreliable RPC, you shouldn’t check for
    collision with them on each peer (otherwise, one peer may be lagging and a player may be considered killed when others aren’t killed).
    The solution is simply to delegate the explosion scene’s player collision check to the server, which will RPC the player.damage() for
    synchronization (Listing 23.7).
    LISTING23.7 Explosion Player Collision Controlled by Master
    111111111
    22222222LISTING23.7 Explosion Player Collision Controlled by Master
    Click here to view code image
    func _ready():

    if not is_network_master():
    return
    # Now that we know which tile is blowing up, retrieve the players
    # and destroy them if they are on it
    for player in get_tree().get_nodes_in_group(‘players’):
    var playerpos = tilemap.world_to_map(player.position)
    if playerpos == tile_pos:
    player.rpc(“damage”, owner.id)
    Finally, you can add a sync keyword to our Player.damage function, and the game should be complete. Congratulations!
    NOTE
    Who’s Master, Who’s Slave?
    Remember, if not configured explicitly, master/slave properties inherit their parent, and the default root scene master is the server. So in our
    game, even if we create bombs with the Players node with various masters, we always add them to the arena scene, which is controlled by the
    server. In the end, it’s the server that is master of all the bomb (and explosion) scenes.
    TRY IT YOURSELF
    Become a Cheater
    It’s easy to exploit when networking, and we will illustrate this by abusing the sync Player.damage() procedure:
    1. Copy your game project in another folder.
    2. Open the “scripts/player.gd” file and modify the _process() function to do an abusing RPC call of the player.damage()
    procedure (see Listing 23.9).
    3. Start the original game as server (you can spawn some clients as well) and start the modified one as client.
    4. Now, try planting a bomb with the malicious server. The other players get killed instantly (and each peer, including the server).
    What happened? As we discussed in Hour 22, sync and remote can be called by any peer (slave or master, it doesn’t matter),
    so you should be careful on what you allow them to trigger.
    LISTING23.9 Player Adds Kill
    Click here to view code image
    func _process(delta):
    if not is_network_master(): # don’t do the exploit on our own player node !
    if Input.is_action_just_pressed(“ui_select”):
    rpc(“damage”, id)

    Summary
    In this hour, we completed another game from scratch! This time, we used the powerful
    AnimationPlayer that can use anything in your
    scene to create animation, and we also mixed the
    TileMap with
    KinematicBody2D players to re-create the NES-era style of
    gameplay. On top of that, we added to the game network capabilities and multiplayer, and saw how to modify its scenes to do synchronization
    in a deterministic way while trying to keep away from cheaters.
    Q&A
    Q. My client cannot join the server.
    A. Make sure you have configured the same port on both client and server. If the two are not on the same computer, you should verify the IP
    address as well and make sure there isn’t a firewall messing with them. (Did we tell you networking is a complicated thing?)
    Q. Can I configure client networking before a server one?
    A. You can configure networking in any order. If client is ready before server, it will poll for it until it is ready. However, keep in mind that a
    timeout will occur if the server takes too long to respond.
    Workshop
    See if you can answer the following questions to test your knowledge.
    Quiz
    111111111
    222222221. What can I animate with a
    AnimationPlayer?
    2. What do I need to use
    TileMap?
    Answers
    1. Every property in any node; it’s that powerful.
    2. You need a
    Tileset that is generated from a scene composed of a
    Sprite. You can also add collision nodes and shapes to
    handle physics. However, keep in mind that you cannot assign scripts to these nodes.
    Exercises
    There are some parts of the game we left on the side to simplify things. Now would be a good time to implement them:
    1. Add variety among the players by giving each a different color.
    2. Handle respawn feature: once hit, a player should move to its initial position, then a blinking animation triggers, during which it cannot
    move.
    3. Create the scoring system: hitting a player gets 100 points, killing yourself makes you lose 50 points. And remember, each feature
    should be synchronized across the network and cheater-free.
    111111111
    22222222