--- /dev/null
+using System.Collections.Generic;
+
+namespace Impostor.Server.GameData
+{
+ public class GameObject
+ {
+ public GameObject()
+ {
+ Components = new List<object>();
+ }
+
+ protected List<object> Components { get; }
+
+ public List<T> GetComponentsInChildren<T>()
+ {
+ var result = new List<T>();
+
+ foreach (var component in Components)
+ {
+ if (component.GetType().IsAssignableTo(typeof(T)))
+ {
+ result.Add((T) component);
+ }
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Net.Messages;
+
+namespace Impostor.Server.GameData
+{
+ public abstract class InnerNetObject : GameObject
+ {
+ public uint NetId { get; set; }
+
+ public int OwnerId { get; set; }
+
+ public SpawnFlags SpawnFlags { get; internal set; }
+
+ public abstract void HandleRpc(byte callId, IMessageReader reader);
+
+ public abstract bool Serialize(IMessageWriter writer, bool initialState);
+
+ public abstract void Deserialize(IMessageReader reader, bool initialState);
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Net.Messages;
+
+namespace Impostor.Server.GameData.Objects.Components
+{
+ public class InnerCustomNetworkTransform : InnerNetObject
+ {
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Net.Messages;
+
+namespace Impostor.Server.GameData.Objects.Components
+{
+ public class InnerPlayerPhysics : InnerNetObject
+ {
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Net.Messages;
+
+namespace Impostor.Server.GameData.Objects.Components
+{
+ public class InnerVoteBanSystem : InnerNetObject
+ {
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.GameData.Objects.Components;
+using Impostor.Server.Games;
+using Impostor.Server.Net.Messages;
+
+namespace Impostor.Server.GameData.Objects
+{
+ public class InnerGameData : InnerNetObject
+ {
+ private readonly IGame _game;
+
+ public InnerGameData(IGame game)
+ {
+ _game = game;
+
+ Components.Add(this);
+ Components.Add(new InnerVoteBanSystem());
+ }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Games;
+using Impostor.Server.Net.Messages;
+
+namespace Impostor.Server.GameData.Objects
+{
+ public class InnerLobbyBehaviour : InnerNetObject
+ {
+ private readonly IGame _game;
+
+ public InnerLobbyBehaviour(IGame game)
+ {
+ _game = game;
+
+ Components.Add(this);
+ }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Games;
+using Impostor.Server.Net.Messages;
+
+namespace Impostor.Server.GameData.Objects
+{
+ public class InnerMeetingHud : InnerNetObject
+ {
+ private readonly IGame _game;
+
+ public InnerMeetingHud(IGame game)
+ {
+ _game = game;
+
+ Components.Add(this);
+ }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.GameData.Objects.Components;
+using Impostor.Server.Games;
+using Impostor.Server.Net.Messages;
+
+namespace Impostor.Server.GameData.Objects
+{
+ public class InnerPlayerControl : InnerNetObject
+ {
+ private readonly IGame _game;
+
+ public InnerPlayerControl(IGame game)
+ {
+ _game = game;
+
+ Components.Add(this);
+ Components.Add(new InnerPlayerPhysics());
+ Components.Add(new InnerCustomNetworkTransform());
+ }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Games;
+using Impostor.Server.Net.Messages;
+
+namespace Impostor.Server.GameData.Objects
+{
+ public class InnerShipStatus : InnerNetObject
+ {
+ private readonly IGame _game;
+
+ public InnerShipStatus(IGame game)
+ {
+ _game = game;
+
+ Components.Add(this);
+ }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+namespace Impostor.Server.GameData
+{
+ [Flags]
+ public enum SpawnFlags : byte
+ {
+ None = 0,
+ IsClientCharacter = 1,
+ }
+}
\ No newline at end of file
</PropertyGroup>
<ItemGroup>
- <ProjectReference Include="..\Impostor.Server.Api\Impostor.Server.Api.csproj"/>
- <ProjectReference Include="..\Impostor.Server.Hazel\Impostor.Server.Hazel.csproj"/>
- <ProjectReference Include="..\Impostor.Shared\Impostor.Shared.csproj"/>
+ <ProjectReference Include="..\Impostor.Server.Api\Impostor.Server.Api.csproj" />
+ <ProjectReference Include="..\Impostor.Server.Hazel\Impostor.Server.Hazel.csproj" />
+ <ProjectReference Include="..\Impostor.Shared\Impostor.Shared.csproj" />
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="5.0.0-rc.1.20451.7"/>
- <PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0-rc.1.20451.14"/>
- <PackageReference Include="Serilog.Extensions.Hosting" Version="3.1.0"/>
- <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1"/>
+ <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="5.0.0-rc.1.20451.7" />
+ <PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0-rc.1.20451.14" />
+ <PackageReference Include="Serilog.Extensions.Hosting" Version="3.1.0" />
+ <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
using System;
-using System.Runtime.InteropServices;
using System.Threading.Tasks;
-using Hazel;
using Impostor.Server.Data;
using Impostor.Server.Games;
using Impostor.Server.Games.Managers;
-using Impostor.Server.Hazel.Messages;
using Impostor.Server.Net.Manager;
using Impostor.Server.Net.Messages;
using Impostor.Shared.Innersloth;
// Handle packet.
var readerCopy = reader.Slice(reader.Position);
+
+ // TODO: Return value, either a bool (to cancel) or a writer (to cancel (null) or modify/overwrite).
await Player.Game.HandleGameData(readerCopy, Player, toPlayer);
// Broadcast packet to all other players.
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Impostor.Server.GameData;
+using Impostor.Server.GameData.Objects;
+using Impostor.Server.Net.Messages;
+using Impostor.Shared.Innersloth.GameData;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Impostor.Server.Net.State
+{
+ internal partial class Game
+ {
+ private static readonly Type[] SpawnableObjects =
+ {
+ typeof(InnerShipStatus), // ShipStatus
+ typeof(InnerMeetingHud),
+ typeof(InnerLobbyBehaviour),
+ typeof(InnerGameData),
+ typeof(InnerPlayerControl),
+ typeof(InnerShipStatus), // HeadQuarters
+ typeof(InnerShipStatus), // PlanetMap
+ typeof(InnerShipStatus), // AprilShipStatus
+ };
+
+ private readonly List<InnerNetObject> _allObjects = new List<InnerNetObject>();
+ private readonly Dictionary<uint, InnerNetObject> _allObjectsFast = new Dictionary<uint, InnerNetObject>();
+
+ public async ValueTask HandleGameData(IMessageReader parent, IClientPlayer sender, bool toPlayer)
+ {
+ // Find target player.
+ IClientPlayer target = null;
+
+ if (toPlayer)
+ {
+ var targetId = parent.ReadPackedInt32();
+ if (!TryGetPlayer(targetId, out target))
+ {
+ // Invalid target.
+ return;
+ }
+ }
+
+ // Parse GameData messages.
+ while (parent.Position < parent.Length)
+ {
+ var reader = parent.ReadMessage();
+
+ switch (reader.Tag)
+ {
+ case GameDataTag.DataFlag:
+ {
+ var objectNetId = reader.ReadPackedUInt32();
+ Logger.Verbose("> Update {0}", objectNetId);
+ break;
+ }
+
+ case GameDataTag.RpcFlag:
+ {
+ var objectNetId = reader.ReadPackedUInt32();
+ var callId = reader.ReadByte();
+ Logger.Verbose("> RPC {0} {1}", objectNetId, callId);
+ break;
+ }
+
+ case GameDataTag.SpawnFlag:
+ {
+ var objectId = reader.ReadPackedUInt32();
+ if (objectId < SpawnableObjects.Length)
+ {
+ var innerNetObject = (InnerNetObject) ActivatorUtilities.CreateInstance(_serviceProvider, SpawnableObjects[objectId], this);
+ var id = reader.ReadPackedInt32();
+
+ innerNetObject.SpawnFlags = (SpawnFlags) reader.ReadByte();
+
+ var components = innerNetObject.GetComponentsInChildren<InnerNetObject>();
+ var componentsCount = reader.ReadPackedInt32();
+
+ if (componentsCount != components.Count)
+ {
+ Logger.Error(
+ "Children didn't match for spawnable {0}, name {1} ({2} != {3})",
+ objectId,
+ innerNetObject.GetType().Name,
+ componentsCount,
+ components.Count);
+ continue;
+ }
+
+ for (var i = 0; i < componentsCount; i++)
+ {
+ var obj = components[i];
+
+ obj.NetId = reader.ReadPackedUInt32();
+ obj.OwnerId = id;
+
+ if (!AddNetObject(obj))
+ {
+ Logger.Verbose("Failed to AddNetObject.");
+
+ obj.NetId = uint.MaxValue;
+ break;
+ }
+
+ var readerSub = reader.ReadMessage();
+ if (readerSub.Length > 0)
+ {
+ obj.Deserialize(readerSub, true);
+ }
+ }
+
+ if ((innerNetObject.SpawnFlags & SpawnFlags.IsClientCharacter) != SpawnFlags.None)
+ {
+ if (TryGetPlayer(id, out var clientById))
+ {
+ Logger.Verbose("Spawn character");
+ }
+ else
+ {
+ Logger.Verbose("Spawn unowned character");
+ }
+ }
+
+ continue;
+ }
+
+ Logger.Error("Couldn't find spawnable object {0}.", objectId);
+ break;
+ }
+
+ case GameDataTag.DespawnFlag:
+ {
+ var objectNetId = reader.ReadPackedUInt32();
+ Logger.Verbose("> Destroy {0}", objectNetId);
+ break;
+ }
+
+ case GameDataTag.SceneChangeFlag:
+ {
+ var clientId = reader.ReadPackedInt32();
+ var targetScene = reader.ReadString();
+ Logger.Verbose("> Scene {0} to {1}", clientId, targetScene);
+ break;
+ }
+
+ case GameDataTag.ReadyFlag:
+ {
+ var clientId = reader.ReadPackedInt32();
+ Logger.Verbose("> IsReady {0}", clientId);
+ break;
+ }
+
+ default:
+ {
+ Logger.Debug("Bad GameData tag {0}", reader.Tag);
+ break;
+ }
+ }
+ }
+ }
+
+ private bool AddNetObject(InnerNetObject obj)
+ {
+ if (_allObjectsFast.ContainsKey(obj.NetId))
+ {
+ return false;
+ }
+
+ _allObjects.Add(obj);
+ _allObjectsFast.Add(obj.NetId, obj);
+ return true;
+ }
+
+ private void RemoveNetObject(InnerNetObject obj)
+ {
+ var index = _allObjects.IndexOf(obj);
+ if (index > -1)
+ {
+ _allObjects.RemoveAt(index);
+ }
+
+ _allObjectsFast.Remove(obj.NetId);
+
+ obj.NetId = uint.MaxValue;
+ }
+ }
+}
\ No newline at end of file
-using System;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using Impostor.Server.Games;
using Impostor.Server.Net.Messages;
using Impostor.Shared.Innersloth.Data;
await BroadcastJoinMessage(packet, true, sender);
}
-
- public async ValueTask HandleGameData(IMessageReader parent, IClientPlayer sender, bool toPlayer)
- {
- // Find target player.
- IClientPlayer target = null;
-
- if (toPlayer)
- {
- var targetId = parent.ReadPackedInt32();
- if (!TryGetPlayer(targetId, out target))
- {
- // Invalid target.
- return;
- }
- }
-
- // Parse GameData messages.
- while (parent.Position < parent.Length)
- {
- var reader = parent.ReadMessage();
-
- switch (reader.Tag)
- {
- case GameDataTag.DataFlag:
- {
- var objectNetId = reader.ReadPackedUInt32();
- Logger.Verbose("> Update {0}", objectNetId);
- break;
- }
-
- case GameDataTag.RpcFlag:
- {
- var objectNetId = reader.ReadPackedUInt32();
- var callId = reader.ReadByte();
- Logger.Verbose("> RPC {0} {1}", objectNetId, callId);
- break;
- }
-
- case GameDataTag.SpawnFlag:
- {
- var objectId = reader.ReadPackedUInt32();
- Logger.Verbose("> Spawn {0}", objectId);
- break;
- }
-
- case GameDataTag.DespawnFlag:
- {
- var objectNetId = reader.ReadPackedUInt32();
- Logger.Verbose("> Destroy {0}", objectNetId);
- break;
- }
-
- case GameDataTag.SceneChangeFlag:
- {
- var clientId = reader.ReadPackedInt32();
- var targetScene = reader.ReadString();
- Logger.Verbose("> Scene {0} to {1}", clientId, targetScene);
- break;
- }
-
- case GameDataTag.ReadyFlag:
- {
- var clientId = reader.ReadPackedInt32();
- Logger.Verbose("> IsReady {0}", clientId);
- break;
- }
-
- default:
- {
- Logger.Debug("Bad GameData tag {0}", reader.Tag);
- break;
- }
- }
- }
- }
}
}
\ No newline at end of file
-using System.Collections.Concurrent;
+using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
{
private static readonly ILogger Logger = Log.ForContext<Game>();
+ private readonly IServiceProvider _serviceProvider;
private readonly IGameManager _gameManager;
private readonly IClientManager _clientManager;
private readonly IMatchmaker _matchmaker;
private readonly HashSet<IPAddress> _bannedIps;
public Game(
+ IServiceProvider serviceProvider,
IGameManager gameManager,
INodeLocator nodeLocator,
IPEndPoint publicIp,
IMatchmaker matchmaker,
IClientManager clientManager)
{
+ _serviceProvider = serviceProvider;
_gameManager = gameManager;
_players = new ConcurrentDictionary<int, IClientPlayer>();
_bannedIps = new HashSet<IPAddress>();