Actual multiplayer woo!

main
copygirl 5 months ago
parent a55e64b73f
commit 94704798a5
  1. 9
      Game.cs
  2. 40
      game.tscn
  3. 7
      player/AnimationController.cs
  4. 4
      player/CameraController.cs
  5. 2
      player/MovementController.cs
  6. 3
      player/Player.cs
  7. 12
      player/Synchronizer.cs
  8. 34
      player/player.tscn
  9. 2
      project.godot
  10. 9
      scenes/workshop.tscn
  11. 81
      scripts/MultiplayerManager.cs
  12. 1
      scripts/globals/GlobalUsings.cs
  13. 30
      ui/MultiplayerMenu.cs

@ -0,0 +1,9 @@
public partial class Game : Node
{
[Export] public Player LocalPlayer { get; set; }
public MultiplayerManager MultiplayerManager { get; private set; }
public override void _EnterTree()
=> MultiplayerManager = GetNode<MultiplayerManager>("MultiplayerManager");
}

@ -1,20 +1,34 @@
[gd_scene load_steps=8 format=3 uid="uid://puuk72ficqhu"]
[gd_scene load_steps=11 format=3 uid="uid://puuk72ficqhu"]
[ext_resource type="PackedScene" uid="uid://bwfuet1irfi17" path="res://scenes/workshop.tscn" id="1_m8m3w"]
[ext_resource type="Shader" path="res://assets/shaders/outline.gdshader" id="2_yvnqw"]
[ext_resource type="Script" path="res://scripts/OutlineCamera.cs" id="3_wd8hf"]
[ext_resource type="Texture2D" uid="uid://lxxfestfg2dt" path="res://assets/crosshair.png" id="4_06ang"]
[ext_resource type="PackedScene" uid="uid://c5ooi36ibspfo" path="res://ui/menu.tscn" id="4_77nbu"]
[ext_resource type="Script" path="res://scripts/Crosshair.cs" id="5_i8gkf"]
[ext_resource type="Script" path="res://scripts/MultiplayerManager.cs" id="1_7shyh"]
[ext_resource type="Script" path="res://Game.cs" id="1_uywdd"]
[ext_resource type="PackedScene" uid="uid://dmd7w2r8s0x6y" path="res://player/player.tscn" id="2_iv2f7"]
[ext_resource type="PackedScene" uid="uid://bwfuet1irfi17" path="res://scenes/workshop.tscn" id="3_4u5ql"]
[ext_resource type="Shader" path="res://assets/shaders/outline.gdshader" id="4_gacvj"]
[ext_resource type="Script" path="res://scripts/OutlineCamera.cs" id="5_qpc14"]
[ext_resource type="PackedScene" uid="uid://c5ooi36ibspfo" path="res://ui/menu.tscn" id="6_ol0j5"]
[ext_resource type="Texture2D" uid="uid://lxxfestfg2dt" path="res://assets/crosshair.png" id="7_0l5tv"]
[ext_resource type="Script" path="res://scripts/Crosshair.cs" id="8_mfhgr"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_ke1l3"]
shader = ExtResource("2_yvnqw")
shader = ExtResource("4_gacvj")
shader_parameter/line_color = Color(1, 1, 1, 0.75)
shader_parameter/line_thickness = 2.0
[node name="Game" type="Node"]
script = ExtResource("1_uywdd")
[node name="Workshop" parent="." instance=ExtResource("1_m8m3w")]
[node name="MultiplayerManager" type="Node" parent="." node_paths=PackedStringArray("LocalPlayer", "Players")]
script = ExtResource("1_7shyh")
LocalPlayer = NodePath("../Players/Local")
Players = NodePath("../Players")
PlayerScene = ExtResource("2_iv2f7")
[node name="Workshop" parent="." instance=ExtResource("3_4u5ql")]
[node name="Players" type="Node3D" parent="."]
[node name="Local" parent="Players" instance=ExtResource("2_iv2f7")]
[node name="OutlineViewportContainer" type="SubViewportContainer" parent="."]
material = SubResource("ShaderMaterial_ke1l3")
@ -34,7 +48,7 @@ render_target_update_mode = 4
[node name="OutlineCamera" type="Camera3D" parent="OutlineViewportContainer/OutlineViewport"]
cull_mask = 2
current = true
script = ExtResource("3_wd8hf")
script = ExtResource("5_qpc14")
[node name="HUD" type="Control" parent="."]
layout_mode = 3
@ -45,7 +59,7 @@ grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="Menu" parent="HUD" instance=ExtResource("4_77nbu")]
[node name="Menu" parent="HUD" instance=ExtResource("6_ol0j5")]
visible = false
layout_mode = 1
anchors_preset = 15
@ -69,5 +83,5 @@ offset_right = 20.0
offset_bottom = 20.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("4_06ang")
script = ExtResource("5_i8gkf")
texture = ExtResource("7_0l5tv")
script = ExtResource("8_mfhgr")

@ -22,11 +22,8 @@ public partial class AnimationController : Node3D
_walkBackwardAnim = _animTree.GetAnimation("walk_backward");
_rootBone = GetNode<BoneAttachment3D>("Root");
foreach (var child in FindChildren("*", "BoneAttachment3D")) {
var bone = (BoneAttachment3D)child;
bone.OverridePose = true;
_bones[bone.Name] = bone;
}
foreach (var bone in FindChildren("*").OfType<BoneAttachment3D>())
{ bone.OverridePose = true; _bones[bone.Name] = bone; }
// We disable the AnimationTree while in the editor so our
// BoneAttackment3D nodes don't get updated, resulting in

@ -7,8 +7,8 @@ public partial class CameraController : Node
public Transform3D DefaultTransform { get; private set; }
public float CurrentPitch { get; set; }
public float CurrentYaw { get; set; }
[Export] public float CurrentPitch { get; set; }
[Export] public float CurrentYaw { get; set; }
public static bool IsMouseCaptured
=> Input.MouseMode == Input.MouseModeEnum.Captured;

@ -16,7 +16,7 @@ public partial class MovementController : Node
public bool IsSprinting { get; private set; }
/// <summary> The raw input movement vector with a maximum length of 1. </summary>
public Vector2 InputVector { get; private set; }
[Export] public Vector2 InputVector { get; private set; }
public bool IsMoving => LocalMoveVector.Length() > 0.01f;
/// <summary> The actual amount the player is moving, relative to the world. Y is always 0. </summary>

@ -1,6 +1,7 @@
public partial class Player : CharacterBody3D
{
[Export] public bool IsLocal { get; set; } = true;
public bool IsLocal { get; set; } = true;
public int PeerId { get; set; }
public MovementController Movement { get; private set; }
public CameraController Camera { get; private set; }

@ -0,0 +1,12 @@
public partial class Synchronizer : MultiplayerSynchronizer
{
// Required because `Velocity` can't be synchronized automatically.
[Export] public Vector3 PlayerVelocity {
get => _player.Velocity;
set => _player.Velocity = value;
}
Player _player;
public override void _Ready()
=> _player = GetParent<Player>();
}

@ -1,4 +1,4 @@
[gd_scene load_steps=24 format=3 uid="uid://dmd7w2r8s0x6y"]
[gd_scene load_steps=26 format=3 uid="uid://dmd7w2r8s0x6y"]
[ext_resource type="PackedScene" uid="uid://bfh3eqgywr0ul" path="res://assets/models/character.blend" id="1_3qh37"]
[ext_resource type="Script" path="res://player/Player.cs" id="1_a0mas"]
@ -7,15 +7,36 @@
[ext_resource type="Script" path="res://player/PickupController.cs" id="2_ns2pe"]
[ext_resource type="Script" path="res://player/CameraController.cs" id="2_r3gna"]
[ext_resource type="Script" path="res://player/AnimationController.cs" id="3_5rlwc"]
[ext_resource type="Script" path="res://player/Synchronizer.cs" id="4_h8li4"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_h1mfd"]
radius = 0.24
height = 1.5
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_l8s0f"]
radius = 0.28
radius = 0.24
height = 1.5
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_dpppx"]
properties/0/path = NodePath(".:position")
properties/0/spawn = true
properties/0/replication_mode = 1
properties/1/path = NodePath(".:rotation")
properties/1/spawn = true
properties/1/replication_mode = 1
properties/2/path = NodePath("MultiplayerSynchronizer:PlayerVelocity")
properties/2/spawn = true
properties/2/replication_mode = 1
properties/3/path = NodePath("MovementController:InputVector")
properties/3/spawn = true
properties/3/replication_mode = 1
properties/4/path = NodePath("CameraController:CurrentPitch")
properties/4/spawn = true
properties/4/replication_mode = 1
properties/5/path = NodePath("CameraController:CurrentYaw")
properties/5/spawn = true
properties/5/replication_mode = 1
[sub_resource type="Animation" id="Animation_5wsog"]
resource_name = "idle_loop"
length = 2.5
@ -784,7 +805,6 @@ input_1/auto_advance = false
input_1/reset = true
[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_cstk8"]
graph_offset = Vector2(30, 1.30002)
nodes/Animation/node = SubResource("AnimationNodeAnimation_ra5r1")
nodes/Animation/position = Vector2(600, 60)
nodes/not_moving/node = SubResource("AnimationNodeAnimation_cun17")
@ -822,6 +842,10 @@ shape = SubResource("CapsuleShape3D_l8s0f")
[node name="Model" parent="." instance=ExtResource("1_3qh37")]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, -0.75, 0)
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
replication_config = SubResource("SceneReplicationConfig_dpppx")
script = ExtResource("4_h8li4")
[node name="MovementController" type="Node" parent="."]
script = ExtResource("2_1pst4")
@ -864,14 +888,14 @@ use_external_skeleton = true
external_skeleton = NodePath("../../../Model/Skeleton/Skeleton3D")
[node name="UpperBody" type="BoneAttachment3D" parent="AnimationController/Root/LowerBody"]
transform = Transform3D(1, -1.08108e-08, 8.67517e-08, 0, 0.992324, 0.123662, -8.74228e-08, -0.123662, 0.992324, 8.88178e-16, 0.154362, 0)
transform = Transform3D(1, -1.08108e-08, 8.67517e-08, 0, 0.992324, 0.123662, -8.74228e-08, -0.123662, 0.992324, 1.77636e-15, 0.154362, 0)
bone_name = "UpperBody"
bone_idx = 4
use_external_skeleton = true
external_skeleton = NodePath("../../../../Model/Skeleton/Skeleton3D")
[node name="Neck" type="BoneAttachment3D" parent="AnimationController/Root/LowerBody/UpperBody"]
transform = Transform3D(1, -4.4409e-16, -7.10543e-15, 0, 0.998891, -0.0470728, 0, 0.0470728, 0.998892, -4.44089e-16, 0.251888, 1.49012e-08)
transform = Transform3D(1, -4.4409e-16, -7.10543e-15, 0, 0.998891, -0.0470728, 0, 0.0470728, 0.998892, -1.11022e-15, 0.251888, 1.49012e-08)
bone_name = "Neck"
bone_idx = 5
use_external_skeleton = true

@ -11,7 +11,7 @@ config_version=5
[application]
config/name="Inventory2"
run/main_scene="res://scenes/game.tscn"
run/main_scene="res://game.tscn"
config/features=PackedStringArray("4.2", "C#", "GL Compatibility")
config/icon="res://assets/icon.svg"

@ -1,6 +1,5 @@
[gd_scene load_steps=14 format=3 uid="uid://bwfuet1irfi17"]
[gd_scene load_steps=13 format=3 uid="uid://bwfuet1irfi17"]
[ext_resource type="PackedScene" uid="uid://dmd7w2r8s0x6y" path="res://player/player.tscn" id="1_cxvln"]
[ext_resource type="Script" path="res://objects/Grid.cs" id="2_gstd0"]
[ext_resource type="PackedScene" uid="uid://yvy5vvaqgxy8" path="res://objects/crate.tscn" id="2_j6a20"]
[ext_resource type="Script" path="res://objects/Item.cs" id="3_01pgc"]
@ -34,12 +33,6 @@ size = Vector2(10, 10)
[node name="Sun" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866025, 0, -0.5, 0.25, 0.866025, 0.433013, 0.433013, -0.5, 0.75, 0, 5, 0)
[node name="Player" parent="." instance=ExtResource("1_cxvln")]
[node name="OtherPlayer" parent="." instance=ExtResource("1_cxvln")]
transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 2, 0.75, -4)
IsLocal = false
[node name="Crates" type="Node3D" parent="."]
[node name="Crate1" parent="Crates" instance=ExtResource("2_j6a20")]

@ -0,0 +1,81 @@
public partial class MultiplayerManager : Node
{
[Export] public Player LocalPlayer { get; set; }
[Export] public Node3D Players { get; set; }
[Export] public PackedScene PlayerScene { get; set; }
public event Action<Player> PlayerJoined;
public event Action<Player> PlayerLeft;
public override void _Ready()
{
Multiplayer.ConnectedToServer += OnMultiplayerReady;
Multiplayer.ServerDisconnected += OnMultiplayerDisconnected;
Multiplayer.PeerConnected += OnPeerConnected;
Multiplayer.PeerDisconnected += OnPeerDisconnected;
}
public void Connect(string address, ushort port)
{
var peer = new ENetMultiplayerPeer();
peer.CreateClient(address, port);
Multiplayer.MultiplayerPeer = peer;
}
public bool CreateServer(ushort port)
{
var peer = new ENetMultiplayerPeer();
if (peer.CreateServer(port) == Error.Ok) {
Multiplayer.MultiplayerPeer = peer;
OnMultiplayerReady();
return true;
} else
return false;
}
public void Disconnect()
=> OnMultiplayerDisconnected();
void OnMultiplayerReady()
{
var localId = Multiplayer.GetUniqueId();
LocalPlayer.Name = localId.ToString();
LocalPlayer.SetMultiplayerAuthority(localId);
if (!Multiplayer.IsServer())
// Spawn players for all the other peers. This excludes the server,
// since `OnPeerConnected` will already be called for it on connecting.
foreach (var peerId in Multiplayer.GetPeers())
if (peerId != 1) OnPeerConnected(peerId);
}
void OnMultiplayerDisconnected()
{
LocalPlayer.Name = "Local";
foreach (var player in Players.GetChildren().Cast<Player>())
OnPeerDisconnected(player.PeerId);
Multiplayer.MultiplayerPeer.Close();
Multiplayer.MultiplayerPeer = null;
}
void OnPeerConnected(long peerId)
{
var player = PlayerScene.Instantiate<Player>();
player.SetMultiplayerAuthority((int)peerId);
player.Name = peerId.ToString();
player.IsLocal = false;
player.PeerId = (int)peerId;
Players.AddChild(player);
PlayerJoined?.Invoke(player);
}
void OnPeerDisconnected(long peerId)
{
var player = Players.GetNode<Player>(peerId.ToString());
PlayerLeft?.Invoke(player);
player.QueueFree();
}
}

@ -1,4 +1,5 @@
global using System;
global using System.Linq;
global using Godot;
global using Godot.Collections;
global using static Godot.GD;

@ -30,16 +30,18 @@ public partial class MultiplayerMenu : MarginContainer
[Export] public Label PlayersLabel { get; set; }
[Export] public Button DisconnectButton { get; set; }
Game _game;
public override void _Ready()
{
_game = GetNode<Game>("/root/Game");
PortDisplay.AddThemeColorOverride("font_uneditable_color", PortDisplay.GetThemeColor("font_color"));
Multiplayer.ConnectedToServer += () => UpdateStatus(Status.Connected);
Multiplayer.ConnectionFailed += () => UpdateStatus(Status.ConnectionFailed);
Multiplayer.ServerDisconnected += () => UpdateStatus(Status.Disconnected);
Multiplayer.PeerConnected += (_) => UpdatePlayerCount();
Multiplayer.PeerDisconnected += (_) => UpdatePlayerCount();
_game.MultiplayerManager.PlayerJoined += (_) => UpdatePlayerCount();
_game.MultiplayerManager.PlayerLeft += (_) => UpdatePlayerCount();
}
void UpdateStatus(Status status)
@ -79,7 +81,7 @@ public partial class MultiplayerMenu : MarginContainer
< Status.Connecting => "Singleplayer",
Status.Connecting => "??? Players",
> Status.Connecting => ((Func<string>)(() => {
var players = Multiplayer.GetPeers().Length + 1;
var players = _game.MultiplayerManager.Players.GetChildCount();
return $"{players} {(players != 1 ? "Players" : "Player")}";
}))(),
};
@ -97,32 +99,24 @@ public partial class MultiplayerMenu : MarginContainer
{
var address = AddressInput.Text;
if (address == "") address = AddressInput.PlaceholderText;
var port = RoundToInt(PortInput.Value);
var peer = new ENetMultiplayerPeer();
peer.CreateClient(address, port);
Multiplayer.MultiplayerPeer = peer;
var port = (ushort)RoundToInt(PortInput.Value);
_game.MultiplayerManager.Connect(address, port);
UpdateStatus(Status.Connecting);
}
public void OnHostPressed()
{
var port = RoundToInt(PortInput.Value);
PortDisplay.Text = port.ToString();
var peer = new ENetMultiplayerPeer();
if (peer.CreateServer(port) == Error.Ok) {
Multiplayer.MultiplayerPeer = peer;
var port = (ushort)RoundToInt(PortInput.Value);
if (_game.MultiplayerManager.CreateServer(port)) {
PortDisplay.Text = port.ToString();
UpdateStatus(Status.Hosting);
} else {
} else
UpdateStatus(Status.HostingFailed);
}
}
public void OnDisconnectPressed()
{
Multiplayer.MultiplayerPeer.Close();
Multiplayer.MultiplayerPeer = null;
_game.MultiplayerManager.Disconnect();
UpdateStatus(Status.Disconnected);
}
}

Loading…
Cancel
Save