Game State Refactor (#586)

* Rename SS14.Server/GameState directory.

* Server now runs at 60 FPS.

* RegisterNetMessage<T> more generic!

Also started refactoring game states.

* HOLY SHIT I CAN MOVE AROUND.

* Dirty(); calls and improvements.

* Implement deletions + mark components as dirty initially.

* Remove bsdiff submodule.

* Remove Bsdiff.

* Fix System.ValueTuple reference.

* Server no longer crashes when the gun is used.

* Optimize player sync.

* Prevent constant dirtiness from player input mover.
This commit is contained in:
Pieter-Jan Briers
2018-05-13 11:40:16 +02:00
committed by GitHub
parent 64e22fc031
commit 0a195fe3c7
61 changed files with 509 additions and 819 deletions

View File

@@ -6,8 +6,4 @@
<RemoveDir Directories="$(OutputPath)Resources" />
<Copy SourceFiles="@(_ResourceFiles)" DestinationFolder="$(OutputPath)Resources\%(RecursiveDir)" />
</Target>
<Target Name="CopyBsdiffWrap">
<Exec Condition="'$(Platform)' == 'x64'" Command="$(Python) ../Tools/download_bsdiffwrap.py $(Platform) $(TargetOS) $(OutputPath)" CustomErrorRegularExpression="^Error" />
<Warning Condition="'$(Platform)' != 'x64'" Text="Did not download bsdiff because the platform is not set to x64. Only use this build for unit testing!" />
</Target>
</Project>

View File

@@ -158,12 +158,11 @@ namespace SS14.Client
Reset();
}
private void HandleServerInfo(NetMessage message)
private void HandleServerInfo(MsgServerInfo msg)
{
if (GameInfo == null)
GameInfo = new ServerInfo();
var msg = (MsgServerInfo)message;
var info = GameInfo;
info.ServerName = msg.ServerName;

View File

@@ -54,7 +54,7 @@ namespace SS14.Client.Console
{
base.Initialize();
_network.RegisterNetMessage<MsgChat>(MsgChat.NAME, msg => HandleChatMsg((MsgChat) msg));
_network.RegisterNetMessage<MsgChat>(MsgChat.NAME, HandleChatMsg);
}
/// <inheritdoc />

View File

@@ -99,17 +99,13 @@ namespace SS14.Client.Console
public event EventHandler<AddStringArgs> AddString;
public event EventHandler ClearText;
private void HandleConCmdAck(NetMessage message)
private void HandleConCmdAck(MsgConCmdAck msg)
{
var msg = (MsgConCmdAck)message;
AddLine("< " + msg.Text, ChatChannel.Default, MsgColor);
}
private void HandleConCmdReg(NetMessage message)
private void HandleConCmdReg(MsgConCmdReg msg)
{
var msg = (MsgConCmdReg)message;
foreach (var cmd in msg.Commands)
{
var commandName = cmd.Name;

View File

@@ -83,6 +83,8 @@ namespace SS14.Client
readonly GameTiming gameTiming;
[Dependency]
readonly IPlacementManager placementManager;
[Dependency]
readonly IClientGameStateManager gameStateManager;
public override void Main(Godot.SceneTree tree)
{
@@ -133,9 +135,7 @@ namespace SS14.Client
placementManager.Initialize();
lightManager.Initialize();
_entityManager.Initialize();
_networkManager.RegisterNetMessage<MsgFullState>(MsgFullState.NAME, message => IoCManager.Resolve<IGameStateManager>().HandleFullStateMessage((MsgFullState)message));
_networkManager.RegisterNetMessage<MsgStateUpdate>(MsgStateUpdate.NAME, message => IoCManager.Resolve<IGameStateManager>().HandleStateUpdateMessage((MsgStateUpdate)message));
gameStateManager.Initialize();
_client.Initialize();

View File

@@ -104,7 +104,7 @@ namespace SS14.Client
IoCManager.Register<IClientNetManager, NetManager>();
IoCManager.Register<IClientEntityManager, ClientEntityManager>();
IoCManager.Register<IEntityNetworkManager, ClientEntityNetworkManager>();
IoCManager.Register<IGameStateManager, GameStateManager>();
IoCManager.Register<IClientGameStateManager, ClientGameStateManager>();
IoCManager.Register<IBaseClient, BaseClient>();
IoCManager.Register<IPlayerManager, PlayerManager>();
IoCManager.Register<IStateManager, StateManager>();

View File

@@ -104,14 +104,12 @@ namespace SS14.Client.GameObjects
Started = true;
}
public void ApplyEntityStates(IEnumerable<EntityState> entityStates, float serverTime)
public void ApplyEntityStates(IEnumerable<EntityState> entityStates, IEnumerable<EntityUid> deletions, float serverTime)
{
var entityKeys = new HashSet<EntityUid>();
foreach (EntityState es in entityStates)
{
//Todo defer component state result processing until all entities are loaded and initialized...
es.ReceivedTime = serverTime;
entityKeys.Add(es.StateData.Uid);
//Known entities
if (Entities.TryGetValue(es.StateData.Uid, out var entity))
{
@@ -129,11 +127,9 @@ namespace SS14.Client.GameObjects
}
}
//Delete entities that exist here but don't exist in the entity states
var toDelete = Entities.Keys.Where(k => !entityKeys.Contains(k)).ToArray();
foreach (var k in toDelete)
foreach (var id in deletions)
{
DeleteEntity(k);
DeleteEntity(id);
}
// After the first set of states comes in we do the startup.

View File

@@ -198,8 +198,6 @@ namespace SS14.Client.GameObjects
private IResourceCache resourceCache;
private IPrototypeManager prototypes;
private int generation;
public int AddLayer(string texturePath)
{
return AddLayer(new ResourcePath(texturePath));
@@ -870,11 +868,6 @@ namespace SS14.Client.GameObjects
public override void HandleComponentState(ComponentState notthestate)
{
var state = (SpriteComponentState)notthestate;
if (state.Generation <= generation)
{
return;
}
generation = state.Generation;
Visible = state.Visible;
DrawDepth = state.DrawDepth;

View File

@@ -0,0 +1,68 @@
using SS14.Client.Interfaces.GameObjects;
using SS14.Client.Interfaces.GameStates;
using SS14.Client.Interfaces.Player;
using SS14.Shared;
using SS14.Shared.GameStates;
using SS14.Shared.Interfaces.Network;
using SS14.Shared.Interfaces.Serialization;
using SS14.Shared.Interfaces.Timing;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Network.Messages;
using System.Collections.Generic;
using System.Linq;
namespace SS14.Client.GameStates
{
public class ClientGameStateManager : IClientGameStateManager
{
private uint GameSequence;
[Dependency]
private readonly IClientNetManager networkManager;
[Dependency]
private readonly IClientEntityManager entityManager;
[Dependency]
private readonly IPlayerManager playerManager;
[Dependency]
private readonly IClientNetManager netManager;
public void Initialize()
{
netManager.RegisterNetMessage<MsgState>(MsgState.NAME, HandleStateMessage);
netManager.RegisterNetMessage<MsgStateAck>(MsgStateAck.NAME);
}
public void HandleStateMessage(MsgState message)
{
var state = message.State;
if (GameSequence < message.State.FromSequence)
{
Logger.ErrorS("net.state", "Got a game state that's too new to handle!");
}
if (GameSequence > message.State.ToSequence)
{
Logger.WarningS("net.state", "Got a game state that's too old to handle!");
}
AckGameState(message.State.ToSequence);
message.State.GameTime = 0;//(float)timing.CurTime.TotalSeconds;
ApplyGameState(message.State);
}
private void AckGameState(uint sequence)
{
var msg = networkManager.CreateNetMessage<MsgStateAck>();
msg.Sequence = sequence;
networkManager.ClientSendMessage(msg);
GameSequence = sequence;
}
private void ApplyGameState(GameState gameState)
{
entityManager.ApplyEntityStates(gameState.EntityStates, gameState.EntityDeletions, gameState.GameTime);
playerManager.ApplyPlayerStates(gameState.PlayerStates);
}
}
}

View File

@@ -1,89 +0,0 @@
using SS14.Client.Interfaces.GameObjects;
using SS14.Client.Interfaces.GameStates;
using SS14.Client.Interfaces.Player;
using SS14.Shared;
using SS14.Shared.GameStates;
using SS14.Shared.Interfaces.Network;
using SS14.Shared.Interfaces.Timing;
using SS14.Shared.IoC;
using SS14.Shared.Network.Messages;
using System.Collections.Generic;
using System.Linq;
namespace SS14.Client.GameStates
{
public class GameStateManager : IGameStateManager
{
public Dictionary<uint, GameState> GameStates { get; set; }
//[Dependency]
//private readonly IGameTiming timing;
[Dependency]
private readonly IClientNetManager networkManager;
[Dependency]
private readonly IClientEntityManager entityManager;
[Dependency]
private readonly IPlayerManager playerManager;
public GameState CurrentState { get; private set; }
public GameStateManager()
{
GameStates = new Dictionary<uint, GameState>();
}
#region Network
public void HandleFullStateMessage(MsgFullState message)
{
if (!GameStates.ContainsKey(message.State.Sequence))
{
AckGameState(message.State.Sequence);
message.State.GameTime = 0;//(float)timing.CurTime.TotalSeconds;
ApplyGameState(message.State);
}
}
public void HandleStateUpdateMessage(MsgStateUpdate message)
{
GameStateDelta delta = message.StateDelta;
if (GameStates.ContainsKey(delta.FromSequence))
{
AckGameState(delta.Sequence);
GameState fromState = GameStates[delta.FromSequence];
GameState newState = fromState + delta;
newState.GameTime = 0;//(float)timing.CurTime.TotalSeconds;
ApplyGameState(newState);
CullOldStates(delta.FromSequence);
}
}
#endregion Network
private void CullOldStates(uint sequence)
{
foreach (uint v in GameStates.Keys.Where(v => v <= sequence).ToList())
GameStates.Remove(v);
}
private void AckGameState(uint sequence)
{
var msg = networkManager.CreateNetMessage<MsgStateAck>();
msg.Sequence = sequence;
networkManager.ClientSendMessage(msg);
}
private void ApplyGameState(GameState gameState)
{
GameStates[gameState.Sequence] = gameState;
CurrentState = gameState;
entityManager.ApplyEntityStates(CurrentState.EntityStates, CurrentState.GameTime);
playerManager.ApplyPlayerStates(CurrentState.PlayerStates);
}
}
}

View File

@@ -12,6 +12,6 @@ namespace SS14.Client.Interfaces.GameObjects
IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Box2 position);
IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Vector2 position);
bool AnyEntitiesIntersecting(MapId mapId, Box2 box);
void ApplyEntityStates(IEnumerable<EntityState> entityStates, float serverTime);
void ApplyEntityStates(IEnumerable<EntityState> entityStates, IEnumerable<EntityUid> deletions, float serverTime);
}
}

View File

@@ -4,9 +4,8 @@ using System.Collections.Generic;
namespace SS14.Client.Interfaces.GameStates
{
public interface IGameStateManager
public interface IClientGameStateManager
{
void HandleFullStateMessage(MsgFullState message);
void HandleStateUpdateMessage(MsgStateUpdate message);
void Initialize();
}
}

View File

@@ -24,6 +24,6 @@ namespace SS14.Client.Interfaces.Player
void Destroy();
//void ApplyEffects(RenderImage image);
void ApplyPlayerStates(List<PlayerState> list);
void ApplyPlayerStates(IEnumerable<PlayerState> list);
}
}

View File

@@ -108,7 +108,7 @@ namespace SS14.Client.Placement
/// Which of the placement orientations we are trying to place with
/// </summary>
public PlacementMode CurrentMode { get; set; }
public PlacementInformation CurrentPermission { get; set; }
private EntityPrototype _currentPrototype;
@@ -147,7 +147,7 @@ namespace SS14.Client.Placement
/// The directional to spawn the entity in
/// </summary>
public Direction Direction { get; set; } = Direction.South;
public Godot.Node2D drawNode { get; set; }
private GodotGlue.GodotSignalSubscriber0 drawNodeDrawSubscriber;
@@ -193,9 +193,8 @@ namespace SS14.Client.Placement
drawNode.Dispose();
}
private void HandlePlacementMessage(NetMessage netMessage)
private void HandlePlacementMessage(MsgPlacement msg)
{
var msg = (MsgPlacement)netMessage;
switch (msg.PlaceType)
{
case PlacementManagerMessage.StartPlacement:
@@ -345,12 +344,12 @@ namespace SS14.Client.Placement
/// <inheritdoc />
public void FrameUpdate(RenderFrameEventArgs e)
{
if(!CurrentMousePosition(out ScreenCoordinates mouseScreen))
if (!CurrentMousePosition(out ScreenCoordinates mouseScreen))
{
return;
}
if(mouseScreen.MapID == MapId.Nullspace)
if (mouseScreen.MapID == MapId.Nullspace)
{
_placenextframe = false;
return;
@@ -401,7 +400,7 @@ namespace SS14.Client.Placement
private void ActivateLineMode(MouseButtonEventArgs e)
{
if(CurrentMode.HasLineMode)
if (CurrentMode.HasLineMode)
{
if (!CurrentMousePosition(out ScreenCoordinates mouseScreen))
{
@@ -431,7 +430,7 @@ namespace SS14.Client.Placement
private bool DeactivateSpecialPlacement()
{
if(_placementType != PlacementType.None)
if (_placementType != PlacementType.None)
{
_placementType = PlacementType.None;
return true;

View File

@@ -43,7 +43,7 @@ namespace SS14.Client.Player
/// Active sessions of connected clients to the server.
/// </summary>
private Dictionary<int, PlayerSession> _sessions;
/// <inheritdoc />
public int PlayerCount => _sessions.Values.Count;
@@ -119,8 +119,13 @@ namespace SS14.Client.Player
*/
/// <inheritdoc />
public void ApplyPlayerStates(List<PlayerState> list)
public void ApplyPlayerStates(IEnumerable<PlayerState> list)
{
if (list == null)
{
// This happens when the server says "nothing changed!"
return;
}
Debug.Assert(_network.IsConnected, "Received player state without being connected?");
Debug.Assert(LocalPlayer != null, "Call Startup()");
Debug.Assert(LocalPlayer.Session != null, "Received player state before Session finished setup.");
@@ -139,10 +144,8 @@ namespace SS14.Client.Player
/// <summary>
/// Handles an incoming session NetMsg from the server.
/// </summary>
private void HandleSessionMessage(NetMessage netMessage)
private void HandleSessionMessage(MsgSession msg)
{
var msg = (MsgSession)netMessage;
switch (msg.MsgType)
{
case PlayerSessionMessage.AttachToEntity:
@@ -180,18 +183,15 @@ namespace SS14.Client.Player
/// <summary>
/// Handles the incoming PlayerList message from the server.
/// </summary>
private void HandlePlayerList(NetMessage netMessage)
private void HandlePlayerList(MsgPlayerList msg)
{
//update sessions with player info
var msg = (MsgPlayerList)netMessage;
UpdatePlayerList(msg.Plyrs);
}
/// <summary>
/// Compares the server player list to the client one, and updates if needed.
/// </summary>
private void UpdatePlayerList(List<PlayerState> remotePlayers)
private void UpdatePlayerList(IEnumerable<PlayerState> remotePlayers)
{
var dirty = false;

View File

@@ -127,7 +127,7 @@
<Compile Include="Console\Commands\HelpCommands.cs" />
<Compile Include="Console\Commands\QuitCommand.cs" />
<Compile Include="Console\Commands\Debug.cs" />
<Compile Include="GameStates\GameStateManager.cs" />
<Compile Include="GameStates\ClientGameStateManager.cs" />
<Compile Include="Graphics\CanvasLayers.cs" />
<Compile Include="Graphics\IDirectionalTextureProvider.cs" />
<Compile Include="Graphics\Lighting\LightManager.Light.cs" />
@@ -146,7 +146,7 @@
<Compile Include="Interfaces\GameObjects\IMoverComponent.cs" />
<Compile Include="Interfaces\GameObjects\IParticleSystemComponent.cs" />
<Compile Include="Interfaces\GameObjects\IRenderableComponent.cs" />
<Compile Include="Interfaces\GameStates\IGameStateManager.cs" />
<Compile Include="Interfaces\GameStates\IClientGameStateManager.cs" />
<Compile Include="Interfaces\Graphics\Lighting\ILight.cs" />
<Compile Include="Interfaces\Graphics\Lighting\ILightManager.cs" />
<Compile Include="Interfaces\Graphics\Lighting\ILightMode.cs" />
@@ -288,5 +288,4 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\MSBuild\SS14.Engine.targets" />
<Target Name="AfterBuild" DependsOnTargets="CopyBsdiffWrap" />
</Project>

View File

@@ -174,10 +174,10 @@ namespace SS14.Client.State.States
if (clickedEntities.Any())
{
entityToClick = (from cd in clickedEntities
orderby cd.drawDepth ascending,
cd.clicked.GetComponent<ITransformComponent>().LocalPosition
.Y ascending
select cd.clicked).Last();
orderby cd.drawDepth ascending,
cd.clicked.GetComponent<ITransformComponent>().LocalPosition
.Y ascending
select cd.clicked).Last();
}
else
{

View File

@@ -69,7 +69,7 @@ namespace SS14.Server
[Dependency]
private readonly ITimerManager timerManager;
[Dependency]
private readonly IGameStateManager _stateManager;
private readonly IServerGameStateManager _stateManager;
[Dependency]
private readonly IServerNetManager _network;
[Dependency]
@@ -208,11 +208,11 @@ namespace SS14.Server
_serializer.Initialize();
// Initialize Tier 2 services
IoCManager.Resolve<IGameStateManager>().Initialize();
IoCManager.Resolve<IEntityManager>().Initialize();
_stateManager.Initialize();
_entities.Initialize();
IoCManager.Resolve<IChatManager>().Initialize();
IoCManager.Resolve<IPlayerManager>().Initialize(MaxPlayers);
IoCManager.Resolve<IMapManager>().Initialize();
_mapManager.Initialize();
IoCManager.Resolve<IPlacementManager>().Initialize();
// Call Init in game assemblies.

View File

@@ -56,7 +56,7 @@ namespace SS14.Server.ClientConsoleHost
AvailableCommands[instance.Command] = instance;
}
_net.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, message => ProcessCommand((MsgConCmd)message));
_net.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
_net.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME);
_net.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, message => HandleRegistrationRequest(message.MsgChannel));
}

View File

@@ -28,7 +28,11 @@ namespace SS14.Server.GameObjects
public Box2 AABB
{
get => _aabb;
set => _aabb = value;
set
{
_aabb = value;
Dirty();
}
}
/// <summary>

View File

@@ -19,31 +19,51 @@ namespace SS14.Server.GameObjects
public Color Color
{
get => _color;
set => _color = value;
set
{
_color = value;
Dirty();
}
}
public LightModeClass Mode
{
get => _mode;
set => _mode = value;
set
{
_mode = value;
Dirty();
}
}
public LightState State
{
get => _state;
set => _state = value;
set
{
_state = value;
Dirty();
}
}
public int Radius
{
get => _radius;
set => _radius = value;
set
{
_radius = value;
Dirty();
}
}
public Vector2 Offset
{
get => _offset;
set => _offset = value;
set
{
_offset = value;
Dirty();
}
}
/// <inheritdoc />

View File

@@ -70,11 +70,12 @@ namespace SS14.Server.GameObjects
var transform = Owner.GetComponent<TransformComponent>();
var physics = Owner.GetComponent<PhysicsComponent>();
physics.LinearVelocity = _moveDir * (_run ? FastMoveSpeed : BaseMoveSpeed);
if (_moveDir.LengthSquared > 0.001)
if (_moveDir.LengthSquared < 0.001)
{
transform.LocalRotation = _moveDir.GetDir().ToAngle();
return;
}
physics.LinearVelocity = _moveDir * (_run ? FastMoveSpeed : BaseMoveSpeed);
transform.LocalRotation = _moveDir.GetDir().ToAngle();
base.Update(frameTime);
}

View File

@@ -29,7 +29,11 @@ namespace SS14.Server.GameObjects
public float Mass
{
get => _mass;
set => _mass = value;
set
{
_mass = value;
Dirty();
}
}
/// <summary>
@@ -38,7 +42,11 @@ namespace SS14.Server.GameObjects
public Vector2 LinearVelocity
{
get => _linVelocity;
set => _linVelocity = value;
set
{
_linVelocity = value;
Dirty();
}
}
/// <summary>

View File

@@ -16,16 +16,6 @@ namespace SS14.Server.GameObjects
public override string Name => "Sprite";
public override uint? NetID => NetIDs.SPRITE;
// So because the game state system is HOT GARBAGE we can't just spam states at the client.
// This kills the client.
// So we basically pre-implement a dirty system.
// We increase this gen for every change.
// If it's different the client actually gives a shit about our component state.
// Else it just ignores it and mourns due to the lost CPU time from bsdiff.
// NOTE: Generation is NOT updated by initial data load. Everything's from the prototype. That's fine.
// This means the client ignores us until there's a *difference*.
private int generation = 0;
private List<Layer> Layers = new List<Layer>();
private bool _visible;
@@ -43,7 +33,7 @@ namespace SS14.Server.GameObjects
set
{
_drawDepth = value;
generation++;
Dirty();
}
}
@@ -53,7 +43,7 @@ namespace SS14.Server.GameObjects
set
{
_visible = value;
generation++;
Dirty();
}
}
@@ -63,7 +53,7 @@ namespace SS14.Server.GameObjects
set
{
_scale = value;
generation++;
Dirty();
}
}
@@ -73,7 +63,7 @@ namespace SS14.Server.GameObjects
set
{
_rotation = value;
generation++;
Dirty();
}
}
@@ -83,7 +73,7 @@ namespace SS14.Server.GameObjects
set
{
_offset = value;
generation++;
Dirty();
}
}
@@ -93,7 +83,7 @@ namespace SS14.Server.GameObjects
set
{
_color = value;
generation++;
Dirty();
}
}
@@ -103,7 +93,7 @@ namespace SS14.Server.GameObjects
set
{
_directional = value;
generation++;
Dirty();
}
}
@@ -113,7 +103,7 @@ namespace SS14.Server.GameObjects
set
{
_baseRSIPath = value;
generation++;
Dirty();
}
}
@@ -122,7 +112,7 @@ namespace SS14.Server.GameObjects
var layer = Layer.New();
layer.TexturePath = texture;
Layers.Add(layer);
generation++;
Dirty();
return Layers.Count - 1;
}
@@ -136,7 +126,7 @@ namespace SS14.Server.GameObjects
var layer = Layer.New();
layer.State = stateId;
Layers.Add(layer);
generation++;
Dirty();
return Layers.Count - 1;
}
@@ -146,7 +136,7 @@ namespace SS14.Server.GameObjects
layer.State = stateId;
layer.RsiPath = rsiPath;
Layers.Add(layer);
generation++;
Dirty();
return Layers.Count - 1;
}
@@ -163,7 +153,7 @@ namespace SS14.Server.GameObjects
return;
}
Layers.RemoveAt(layer);
generation++;
Dirty();
}
public void LayerSetShader(int layer, string shaderName)
@@ -176,7 +166,7 @@ namespace SS14.Server.GameObjects
var thelayer = Layers[layer];
thelayer.Shader = shaderName;
Layers[layer] = thelayer;
generation++;
Dirty();
}
public void LayerSetTexture(int layer, string texturePath)
@@ -190,7 +180,7 @@ namespace SS14.Server.GameObjects
thelayer.State = null;
thelayer.TexturePath = texturePath;
Layers[layer] = thelayer;
generation++;
Dirty();
}
public void LayerSetTexture(int layer, ResourcePath texturePath)
{
@@ -208,7 +198,7 @@ namespace SS14.Server.GameObjects
thelayer.State = stateId;
thelayer.TexturePath = null;
Layers[layer] = thelayer;
generation++;
Dirty();
}
public void LayerSetState(int layer, string stateId, string rsiPath)
@@ -223,7 +213,7 @@ namespace SS14.Server.GameObjects
thelayer.State = stateId;
thelayer.TexturePath = null;
Layers[layer] = thelayer;
generation++;
Dirty();
}
public void LayerSetState(int layer, string stateId, ResourcePath rsiPath)
@@ -242,7 +232,7 @@ namespace SS14.Server.GameObjects
var thelayer = Layers[layer];
thelayer.RsiPath = rsiPath;
Layers[layer] = thelayer;
generation++;
Dirty();
}
public void LayerSetRSI(int layer, ResourcePath rsiPath)
@@ -261,7 +251,7 @@ namespace SS14.Server.GameObjects
var thelayer = Layers[layer];
thelayer.Scale = scale;
Layers[layer] = thelayer;
generation++;
Dirty();
}
public void LayerSetRotation(int layer, Angle rotation)
@@ -275,7 +265,7 @@ namespace SS14.Server.GameObjects
var thelayer = Layers[layer];
thelayer.Rotation = rotation;
Layers[layer] = thelayer;
generation++;
Dirty();
}
public void LayerSetVisible(int layer, bool visible)
@@ -289,7 +279,7 @@ namespace SS14.Server.GameObjects
var thelayer = Layers[layer];
thelayer.Visible = visible;
Layers[layer] = thelayer;
generation++;
Dirty();
}
public override void ExposeData(EntitySerializer serializer)
@@ -414,7 +404,7 @@ namespace SS14.Server.GameObjects
public override ComponentState GetComponentState()
{
var list = Layers.Select((l) => l.ToStateLayer()).ToList();
return new SpriteComponentState(generation, Visible, DrawDepth, Scale, Rotation, Offset, Color, Directional, BaseRSIPath, list);
return new SpriteComponentState(Visible, DrawDepth, Scale, Rotation, Offset, Color, Directional, BaseRSIPath, list);
}
private struct Layer

View File

@@ -92,6 +92,7 @@ namespace SS14.Server.GameObjects
_rotation = value;
RebuildMatrices();
OnRotate?.Invoke(value);
Dirty();
}
}
@@ -166,6 +167,8 @@ namespace SS14.Server.GameObjects
GridID = value.GridID;
}
Dirty();
RebuildMatrices();
OnMove?.Invoke(this, new MoveEventArgs(LocalPosition, value));
}
@@ -205,6 +208,8 @@ namespace SS14.Server.GameObjects
GridID = IoCManager.Resolve<IMapManager>().GetMap(MapID).FindGridAt(_position).Index;
}
Dirty();
RebuildMatrices();
OnMove?.Invoke(this, new MoveEventArgs(LocalPosition, new LocalCoordinates(_position, GridID, MapID)));
}
@@ -248,6 +253,8 @@ namespace SS14.Server.GameObjects
// switch position back to grid coords
LocalPosition = lc;
Dirty();
}
/// <summary>
@@ -269,6 +276,7 @@ namespace SS14.Server.GameObjects
// offset position from world to parent
_position = MatMult(parent.InvWorldMatrix, _position);
RebuildMatrices();
Dirty();
}
public void AttachParent(IEntity entity)

View File

@@ -37,6 +37,11 @@ namespace SS14.Server.GameObjects.EntitySystems
var transform = entity.GetComponent<TransformComponent>();
var velocity = entity.GetComponent<PhysicsComponent>();
if (velocity.AngularVelocity == 0 && velocity.LinearVelocity == Vector2.Zero)
{
return;
}
//rotate entity
float angImpulse = 0;
if (velocity.AngularVelocity > Epsilon)
@@ -59,7 +64,7 @@ namespace SS14.Server.GameObjects.EntitySystems
if (collided)
{
if(velocity.EdgeSlide)
if (velocity.EdgeSlide)
{
//Slide along the blockage in the non-blocked direction
var xBlocked = collider.TryCollision(new Vector2(movement.X, 0));

View File

@@ -24,6 +24,8 @@ namespace SS14.Server.GameObjects
[Dependency]
private readonly IMapManager _mapManager;
private List<(uint tick, EntityUid uid)> DeletionHistory = new List<(uint, EntityUid)>();
/// <inheritdoc />
public bool TrySpawnEntityAt(string entityType, LocalCoordinates coordinates, out IEntity entity)
{
@@ -75,17 +77,47 @@ namespace SS14.Server.GameObjects
}
/// <inheritdoc />
public List<EntityState> GetEntityStates()
public List<EntityState> GetEntityStates(uint fromTick)
{
var stateEntities = new List<EntityState>();
foreach (IEntity entity in GetEntities())
{
EntityState entityState = entity.GetEntityState();
if (entity.LastModifiedTick < fromTick)
{
continue;
}
EntityState entityState = entity.GetEntityState(fromTick);
stateEntities.Add(entityState);
}
return stateEntities;
}
public override void DeleteEntity(IEntity e)
{
base.DeleteEntity(e);
DeletionHistory.Add((CurrentTick, e.Uid));
}
public List<EntityUid> GetDeletedEntities(uint fromTick)
{
List<EntityUid> list = new List<EntityUid>();
foreach ((var tick, var id) in DeletionHistory)
{
if (tick >= fromTick)
{
list.Add(id);
}
}
return list;
}
public void CullDeletionHistory(uint toTick)
{
DeletionHistory.RemoveAll(hist => hist.tick <= toTick);
}
/// <inheritdoc />
public void SaveGridEntities(EntitySerializer serializer, GridId gridId)
{
@@ -180,7 +212,7 @@ namespace SS14.Server.GameObjects
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInRange(MapId mapID, Box2 box, float range)
{
var aabb = new Box2(box.Left-range, box.Top-range, box.Right+range, box.Bottom+range);
var aabb = new Box2(box.Left - range, box.Top - range, box.Right + range, box.Bottom + range);
return GetEntitiesIntersecting(mapID, aabb);
}
@@ -202,8 +234,8 @@ namespace SS14.Server.GameObjects
public IEnumerable<IEntity> GetEntitiesInArc(LocalCoordinates coordinates, float range, Angle direction, float arcwidth)
{
var entities = GetEntitiesInRange(coordinates, range);
foreach(var entity in entities)
foreach (var entity in entities)
{
var angle = new Angle(entity.GetComponent<TransformComponent>().WorldPosition - coordinates.ToWorld().Position);
if (angle.Degrees < direction.Degrees + arcwidth / 2 && angle.Degrees > direction.Degrees - arcwidth / 2)

View File

@@ -1,142 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using SS14.Server.Interfaces.GameObjects;
using SS14.Server.Interfaces.GameState;
using SS14.Server.Interfaces.Player;
using SS14.Shared.Enums;
using SS14.Shared.GameStates;
using SS14.Shared.Interfaces.Network;
using SS14.Shared.Interfaces.Timing;
using SS14.Shared.IoC;
using SS14.Shared.Network.Messages;
namespace SS14.Server.GameStates
{
public class GameStateManager : Dictionary<uint, GameState>, IGameStateManager
{
private readonly Dictionary<long, uint> ackedStates = new Dictionary<long, uint>();
[Dependency]
private IServerEntityManager _entityManager;
[Dependency]
private IGameTiming _gameTiming;
[Dependency]
private IServerNetManager _networkManager;
[Dependency]
private IPlayerManager _playerManager;
public void Initialize()
{
_networkManager.RegisterNetMessage<MsgStateUpdate>(MsgStateUpdate.NAME);
_networkManager.RegisterNetMessage<MsgStateAck>(MsgStateAck.NAME, message => HandleStateAck((MsgStateAck) message));
_networkManager.RegisterNetMessage<MsgFullState>(MsgFullState.NAME);
}
public void Cull()
{
foreach (var v in Keys.Where(v => v < OldestStateAcked).ToList())
Remove(v);
}
public uint OldestStateAcked
{
get { return ackedStates.Values.FirstOrDefault(val => val == ackedStates.Values.Min()); }
}
public void Ack(long uniqueIdentifier, uint stateAcked)
{
if (!ackedStates.ContainsKey(uniqueIdentifier))
ackedStates.Add(uniqueIdentifier, stateAcked);
else
ackedStates[uniqueIdentifier] = stateAcked;
}
public GameStateDelta GetDelta(INetChannel client, uint state)
{
var toState = GetFullState(state);
if (!ackedStates.ContainsKey(client.ConnectionId))
return toState - new GameState(0); //The client has no state!
var ackack = ackedStates[client.ConnectionId];
var fromState = this[ackack];
return toState - fromState;
}
public GameState GetFullState(uint state)
{
if (ContainsKey(state))
return this[state];
return null; //TODO SHIT
}
public uint GetLastStateAcked(INetChannel client)
{
if (!ackedStates.ContainsKey(client.ConnectionId))
ackedStates[client.ConnectionId] = 0;
return ackedStates[client.ConnectionId];
}
public void CullAll()
{
ackedStates.Clear();
Clear();
}
public void SendGameStateUpdate()
{
//Create a new GameState object
var state1 = new GameState(_gameTiming.CurTick)
{
EntityStates = _entityManager.GetEntityStates(),
PlayerStates = _playerManager.GetPlayerStates()
};
var state = state1;
Add(state.Sequence, state);
var connections = _networkManager.Channels;
if (!connections.Any())
{
CullAll();
return;
}
var playerMan = _playerManager;
foreach (var c in connections)
{
var session = playerMan.GetSessionByChannel(c);
if (session == null || session.Status != SessionStatus.InGame && session.Status != SessionStatus.InLobby)
continue;
SendConnectionGameStateUpdate(c, state, _gameTiming.CurTick);
}
Cull();
}
private void SendConnectionGameStateUpdate(INetChannel c, GameState state, uint curTick)
{
if (((IGameStateManager) this).GetLastStateAcked(c) == 0)
{
var fullStateMessage = _networkManager.CreateNetMessage<MsgFullState>();
fullStateMessage.State = state;
_networkManager.ServerSendMessage(fullStateMessage, c);
}
else
{
var stateUpdateMessage = _networkManager.CreateNetMessage<MsgStateUpdate>();
stateUpdateMessage.StateDelta = ((IGameStateManager) this).GetDelta(c, curTick);
_networkManager.ServerSendMessage(stateUpdateMessage, c);
}
}
private void HandleStateAck(MsgStateAck msg)
{
Ack(msg.MsgChannel.ConnectionId, msg.Sequence);
}
}
}

View File

@@ -0,0 +1,98 @@
using System.Collections.Generic;
using System.Linq;
using SS14.Server.Interfaces.GameObjects;
using SS14.Server.Interfaces.GameState;
using SS14.Server.Interfaces.Player;
using SS14.Shared.Enums;
using SS14.Shared.GameStates;
using SS14.Shared.Interfaces.Network;
using SS14.Shared.Interfaces.Timing;
using SS14.Shared.IoC;
using SS14.Shared.Network.Messages;
namespace SS14.Server.GameStates
{
public class ServerGameStateManager : IServerGameStateManager
{
// Mapping of net UID of clients -> last known acked state.
private readonly Dictionary<long, uint> ackedStates = new Dictionary<long, uint>();
private uint lastOldestAck = 0;
[Dependency]
private IServerEntityManager _entityManager;
[Dependency]
private IGameTiming _gameTiming;
[Dependency]
private IServerNetManager _networkManager;
[Dependency]
private IPlayerManager _playerManager;
public void Initialize()
{
_networkManager.RegisterNetMessage<MsgState>(MsgState.NAME);
_networkManager.RegisterNetMessage<MsgStateAck>(MsgStateAck.NAME, HandleStateAck);
}
private void Ack(long uniqueIdentifier, uint stateAcked)
{
ackedStates[uniqueIdentifier] = stateAcked;
}
public void SendGameStateUpdate()
{
var connections = _networkManager.Channels;
if (!connections.Any())
{
// Prevent deletions piling up if we have no clients.
_entityManager.CullDeletionHistory(uint.MaxValue);
return;
}
uint oldestAck = uint.MaxValue;
foreach (var connection in connections)
{
if (!ackedStates.TryGetValue(connection.ConnectionId, out var ack))
{
ackedStates.Add(connection.ConnectionId, 0);
}
else if (ack < oldestAck)
{
oldestAck = ack;
}
}
if (oldestAck > lastOldestAck)
{
lastOldestAck = oldestAck;
_entityManager.CullDeletionHistory(oldestAck);
}
var entities = _entityManager.GetEntityStates(oldestAck);
var players = _playerManager.GetPlayerStates();
var deletions = _entityManager.GetDeletedEntities(oldestAck);
var state = new GameState(oldestAck, _gameTiming.CurTick, entities, players, deletions);
foreach (var c in connections)
{
var session = _playerManager.GetSessionByChannel(c);
if (session == null || session.Status != SessionStatus.InGame && session.Status != SessionStatus.InLobby)
continue;
var stateUpdateMessage = _networkManager.CreateNetMessage<MsgState>();
stateUpdateMessage.State = state;
_networkManager.ServerSendMessage(stateUpdateMessage, c);
}
}
private void HandleStateAck(MsgStateAck msg)
{
Ack(msg.MsgChannel.ConnectionId, msg.Sequence);
}
}
}

View File

@@ -10,7 +10,7 @@ namespace SS14.Server.Interfaces.GameObjects
public interface IServerEntityManager : IEntityManager
{
Entity SpawnEntity(string template, EntityUid? uid = null);
/// <summary>
/// Spawns an entity at a specific position
/// </summary>
@@ -104,7 +104,22 @@ namespace SS14.Server.Interfaces.GameObjects
/// <returns></returns>
IEnumerable<IEntity> GetEntitiesInArc(LocalCoordinates coordinates, float range, Angle direction, float arcwidth);
List<EntityState> GetEntityStates();
/// <summary>
/// Gets all entity states that have been modified after and including the provided tick.
/// </summary>
List<EntityState> GetEntityStates(uint fromTick);
// Keep track of deleted entities so we can sync deletions with the client.
/// <summary>
/// Gets a list of all entity UIDs that were deleted between <paramref name="fromTick" /> and now.
/// </summary>
List<EntityUid> GetDeletedEntities(uint fromTick);
/// <summary>
/// Remove deletion history.
/// </summary>
/// <param name="toTick">The last tick to delete the history for. Inclusive.</param>
void CullDeletionHistory(uint toTick);
/// <summary>
/// Serializes all entities on a grid.

View File

@@ -1,19 +0,0 @@
using System.Collections.Generic;
using SS14.Shared.GameStates;
using SS14.Shared.Interfaces.Network;
namespace SS14.Server.Interfaces.GameState
{
public interface IGameStateManager : IDictionary<uint, Shared.GameStates.GameState>
{
uint OldestStateAcked { get; }
void Initialize();
void Cull();
void Ack(long uniqueIdentifier, uint state);
GameStateDelta GetDelta(INetChannel client, uint state);
Shared.GameStates.GameState GetFullState(uint state);
uint GetLastStateAcked(INetChannel client);
void CullAll();
void SendGameStateUpdate();
}
}

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
using SS14.Shared.GameStates;
using SS14.Shared.Interfaces.Network;
namespace SS14.Server.Interfaces.GameState
{
public interface IServerGameStateManager
{
void Initialize();
void SendGameStateUpdate();
}
}

View File

@@ -41,7 +41,7 @@ namespace SS14.Server.Placement
public void Initialize()
{
_networkManager.RegisterNetMessage<MsgPlacement>(MsgPlacement.NAME, message => HandleNetMessage((MsgPlacement)message));
_networkManager.RegisterNetMessage<MsgPlacement>(MsgPlacement.NAME, HandleNetMessage);
}
/// <summary>
@@ -91,7 +91,7 @@ namespace SS14.Server.Placement
var mapIndex = plyTransform.MapID;
// no building in null space!
if(mapIndex == MapId.Nullspace)
if (mapIndex == MapId.Nullspace)
return;
//TODO: Distance check, so you can't place things off of screen.

View File

@@ -31,6 +31,8 @@ namespace SS14.Server.Player
[Dependency]
private readonly IServerNetManager _network;
private bool NeedsStateUpdate = false;
/// <summary>
/// Number of active sessions.
/// This is the cached value of _sessions.Count(s => s != null);
@@ -61,7 +63,7 @@ namespace SS14.Server.Player
_sessions = new PlayerSession[maxPlayers];
_network.RegisterNetMessage<MsgSession>(MsgSession.NAME);
_network.RegisterNetMessage<MsgClGreet>(MsgClGreet.NAME, message => HandleClientGreet((MsgClGreet)message));
_network.RegisterNetMessage<MsgClGreet>(MsgClGreet.NAME, HandleClientGreet);
_network.RegisterNetMessage<MsgServerInfoReq>(MsgServerInfoReq.NAME, HandleWelcomeMessageReq);
_network.RegisterNetMessage<MsgServerInfo>(MsgServerInfo.NAME);
_network.RegisterNetMessage<MsgPlayerListReq>(MsgPlayerListReq.NAME, HandlePlayerListReq);
@@ -168,7 +170,7 @@ namespace SS14.Server.Player
/// <returns></returns>
public List<IPlayerSession> GetAllPlayers()
{
return _sessions.Where(x => x!= null).Cast<IPlayerSession>().ToList();
return _sessions.Where(x => x != null).Cast<IPlayerSession>().ToList();
}
/// <summary>
@@ -177,6 +179,11 @@ namespace SS14.Server.Player
/// <returns></returns>
public List<PlayerState> GetPlayerStates()
{
if (!NeedsStateUpdate)
{
return null;
}
NeedsStateUpdate = false;
return _sessions
.Where(s => s != null)
.Select(s => s.PlayerState)
@@ -261,10 +268,10 @@ namespace SS14.Server.Player
p.SetName(fixedName);
}
private void HandleWelcomeMessageReq(NetMessage message)
private void HandleWelcomeMessageReq(MsgServerInfoReq message)
{
var session = GetSessionByChannel(message.MsgChannel);
session.Name = ((MsgServerInfoReq)message).PlayerName;
session.Name = message.PlayerName;
var netMsg = message.MsgChannel.CreateNetMessage<MsgServerInfo>();
@@ -280,7 +287,7 @@ namespace SS14.Server.Player
message.MsgChannel.SendMessage(netMsg);
}
private void HandlePlayerListReq(NetMessage message)
private void HandlePlayerListReq(MsgPlayerListReq message)
{
var channel = message.MsgChannel;
var players = GetAllPlayers().ToArray();
@@ -311,6 +318,11 @@ namespace SS14.Server.Player
var session = GetSessionByChannel(channel);
session.Status = SessionStatus.Connected;
}
public void Dirty()
{
NeedsStateUpdate = true;
}
}
public class SessionStatusEventArgs : EventArgs

View File

@@ -212,6 +212,8 @@ namespace SS14.Server.Player
PlayerState.ControlledEntity = null;
else
PlayerState.ControlledEntity = AttachedEntity.Uid;
_playerManager.Dirty();
}
/// <inheritdoc />

View File

@@ -122,7 +122,7 @@ namespace SS14.Server
IoCManager.Register<ISS14Serializer, SS14Serializer>();
IoCManager.Register<IEntityNetworkManager, ServerEntityNetworkManager>();
IoCManager.Register<ICommandLineArgs, CommandLineArgs>();
IoCManager.Register<IGameStateManager, GameStateManager>();
IoCManager.Register<IServerGameStateManager, ServerGameStateManager>();
IoCManager.Register<IReflectionManager, ServerReflectionManager>();
IoCManager.Register<IClientConsoleHost, ClientConsoleHost.ClientConsoleHost>();
IoCManager.Register<IPlayerManager, PlayerManager>();

View File

@@ -91,7 +91,7 @@
<Private>False</Private>
</Reference>
<Reference Include="System.ValueTuple">
<HintPath>$(SolutionDir)packages\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
<HintPath>$(SolutionDir)packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Xml">
<Name>System.Xml</Name>
@@ -166,7 +166,7 @@
<Compile Include="GameObjects\EntitySystems\EffectSystem.cs" />
<Compile Include="GameObjects\EntitySystems\PhysicsSystem.cs" />
<Compile Include="GameObjects\EntitySystems\InputSystem.cs" />
<Compile Include="GameState\GameStateManager.cs" />
<Compile Include="GameStates\ServerGameStateManager.cs" />
<Compile Include="Interfaces\ICommandLineArgs.cs" />
<Compile Include="Interfaces\IBaseServer.cs" />
<Compile Include="Interfaces\Chat\IChatCommand.cs" />
@@ -179,7 +179,7 @@
<Compile Include="Interfaces\GameObjects\IParticleSystemComponent.cs" />
<Compile Include="Interfaces\GameObjects\IRenderableComponent.cs" />
<Compile Include="Interfaces\GameObjects\ISpriteRenderableComponent.cs" />
<Compile Include="Interfaces\GameState\IGameStateManager.cs" />
<Compile Include="Interfaces\GameState\IServerGameStateManager.cs" />
<Compile Include="Interfaces\Placement\IPlacementManager.cs" />
<Compile Include="Interfaces\Player\IPlayerManager.cs" />
<Compile Include="Interfaces\Player\IPlayerSession.cs" />
@@ -206,5 +206,4 @@
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<Target Name="AfterBuild" DependsOnTargets="CopyBsdiffWrap" />
</Project>

View File

@@ -6,7 +6,7 @@ level = 1
enabled = false
[net]
tickrate = 66
tickrate = 60
port = 1212
allowdupeip = true

View File

@@ -1,4 +1,5 @@
using System;
using System.Runtime.CompilerServices;
using SS14.Shared.GameObjects.Serialization;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.Interfaces.Network;
@@ -32,6 +33,8 @@ namespace SS14.Shared.GameObjects
/// <inheritdoc />
public bool Deleted { get; private set; }
public uint LastModifiedTick { get; private set; }
/// <inheritdoc />
public virtual void OnRemove()
{
@@ -73,6 +76,15 @@ namespace SS14.Shared.GameObjects
[Obsolete("Components should be updated through a system.")]
public virtual void Update(float frameTime) { }
public void Dirty()
{
if (Owner != null)
{
Owner.Dirty();
LastModifiedTick = Owner.EntityManager.CurrentTick;
}
}
/// <summary>
/// Sends a message to all other components in this entity.
/// This is an alias of 'Owner.SendMessage(this, message);'

View File

@@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
namespace SS14.Shared.GameObjects.Components.Hands
{
[Serializable]
public class HandsComponentState : ComponentState
{
public InventoryLocation ActiveHand;
public Dictionary<InventoryLocation, int?> Slots;
public HandsComponentState(InventoryLocation _ActiveHand, Dictionary<InventoryLocation, int?> _Slots)
: base(ComponentFamily.Hands)
{
ActiveHand = _ActiveHand;
Slots = _Slots;
}
}
}

View File

@@ -7,7 +7,6 @@ namespace SS14.Shared.GameObjects
[Serializable]
public class SpriteComponentState : ComponentState
{
public readonly int Generation;
public readonly bool Visible;
public readonly DrawDepth DrawDepth;
public readonly Vector2 Scale;
@@ -19,7 +18,6 @@ namespace SS14.Shared.GameObjects
public readonly List<Layer> Layers;
public SpriteComponentState(
int generation,
bool visible,
DrawDepth drawDepth,
Vector2 scale,
@@ -31,7 +29,6 @@ namespace SS14.Shared.GameObjects
List<Layer> layers)
: base(NetIDs.SPRITE)
{
Generation = generation;
Visible = visible;
DrawDepth = drawDepth;
Scale = scale;

View File

@@ -52,11 +52,17 @@ namespace SS14.Shared.GameObjects
/// </summary>
private string _description;
public uint LastModifiedTick { get; private set; }
/// <inheritdoc />
public string Name
{
get => _name;
set => _name = value;
set
{
_name = value;
Dirty();
}
}
/// <inheritdoc />
@@ -330,6 +336,7 @@ namespace SS14.Shared.GameObjects
if (component.NetID != null)
{
_netIDs[component.NetID.Value] = component;
component.Dirty();
}
// Register the component with the ComponentManager.
@@ -392,7 +399,10 @@ namespace SS14.Shared.GameObjects
}
if (component.NetID != null)
{
_netIDs.Remove(component.NetID.Value);
Dirty();
}
}
/// <inheritdoc />
@@ -527,9 +537,9 @@ namespace SS14.Shared.GameObjects
}
/// <inheritdoc />
public EntityState GetEntityState()
public EntityState GetEntityState(uint fromTick)
{
var compStates = GetComponentStates();
var compStates = GetComponentStates(fromTick);
var synchedComponentTypes = _netIDs
.Where(t => t.Value.NetworkSynchronizeExistence)
.Select(t => new Tuple<uint, string>(t.Key, t.Value.Name))
@@ -548,14 +558,19 @@ namespace SS14.Shared.GameObjects
/// Server-side method to get the state of all our components
/// </summary>
/// <returns></returns>
private List<ComponentState> GetComponentStates()
private List<ComponentState> GetComponentStates(uint fromTick)
{
return GetAllComponents()
.Where(c => c.NetID != null)
.Where(c => c.NetID != null && c.LastModifiedTick >= fromTick)
.Select(component => component.GetComponentState())
.ToList();
}
public void Dirty()
{
LastModifiedTick = EntityManager.CurrentTick;
}
#endregion GameState Stuff
}
}

View File

@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using SS14.Shared.Interfaces.Network;
using Vector2 = SS14.Shared.Maths.Vector2;
using SS14.Shared.Interfaces.Timing;
namespace SS14.Shared.GameObjects
{
@@ -28,7 +29,12 @@ namespace SS14.Shared.GameObjects
private readonly INetManager _network;
[Dependency]
private readonly IComponentManager _componentManager;
# endregion Dependencies
[Dependency]
private readonly IGameTiming _gameTiming;
#endregion Dependencies
public uint CurrentTick => _gameTiming.CurTick;
protected readonly Dictionary<EntityUid, IEntity> Entities = new Dictionary<EntityUid, IEntity>();
/// <summary>
@@ -55,7 +61,7 @@ namespace SS14.Shared.GameObjects
public virtual void Initialize()
{
_network.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, message => HandleEntityNetworkMessage((MsgEntity)message));
_network.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, HandleEntityNetworkMessage);
}
public virtual void Startup()
@@ -149,7 +155,7 @@ namespace SS14.Shared.GameObjects
/// Shuts-down and removes given Entity. This is also broadcast to all clients.
/// </summary>
/// <param name="e">Entity to remove</param>
public void DeleteEntity(IEntity e)
public virtual void DeleteEntity(IEntity e)
{
e.Shutdown();
}

View File

@@ -23,7 +23,7 @@ namespace SS14.Shared.GameObjects
SetStateData(new EntityStateData(uid, templateName, name, synchedComponentTypes));
ComponentStates = componentStates;
}
public void SetStateData(EntityStateData data)
{
StateData = data;

View File

@@ -11,21 +11,7 @@ namespace SS14.Shared.GameStates
[Serializable, NetSerializable]
public class GameState
{
[NonSerialized] private bool _serialized;
[NonSerialized] private MemoryStream _serializedData;
[NonSerialized] private float _gameTime;
/// <summary>
/// Constructor!
/// </summary>
/// <param name="sequence"></param>
public GameState(uint sequence)
{
Sequence = sequence;
}
public uint Sequence { get; private set; }
public List<EntityState> EntityStates { get; set; }
public List<PlayerState> PlayerStates { get; set; }
public float GameTime
{
@@ -34,104 +20,23 @@ namespace SS14.Shared.GameStates
}
/// <summary>
/// Creates a delta from two game states
/// Constructor!
/// </summary>
/// <param name="toState"></param>
/// <param name="fromState"></param>
/// <returns></returns>
public static GameStateDelta operator -(GameState toState, GameState fromState)
/// <param name="sequence"></param>
public GameState(uint fromSequence, uint toSequence, List<EntityState> entities, List<PlayerState> players, List<EntityUid> deletions)
{
return Delta(fromState, toState);
FromSequence = fromSequence;
ToSequence = toSequence;
EntityStates = entities;
PlayerStates = players;
EntityDeletions = deletions;
}
/// <summary>
/// Applies a delta to a game state
/// </summary>
/// <param name="fromState"></param>
/// <param name="delta"></param>
/// <returns></returns>
public static GameState operator +(GameState fromState, GameStateDelta delta)
{
return Patch(fromState, delta);
}
public readonly uint FromSequence;
public readonly uint ToSequence;
/// <summary>
/// Applies a delta to a game state
/// </summary>
/// <param name="delta"></param>
/// <param name="fromState"></param>
/// <returns></returns>
public static GameState operator +(GameStateDelta delta, GameState fromState)
{
return Patch(fromState, delta);
}
public static GameStateDelta Delta(GameState fromState, GameState toState)
{
var delta = new GameStateDelta();
delta.Sequence = toState.Sequence;
delta.Create(fromState, toState);
return delta;
}
public static GameState Patch(GameState fromState, GameStateDelta delta)
{
return delta.Apply(fromState);
}
/// <summary>
/// Serializes the game state to its buffer -- allows us to serialize only once, saving some processor time
/// </summary>
private void Serialize()
{
if (_serialized)
return;
_serializedData = new MemoryStream();
var serializer = IoCManager.Resolve<ISS14Serializer>();
serializer.Serialize(_serializedData, this);
_serialized = true;
}
/// <summary>
/// Returns the serialized game state data stream
/// </summary>
/// <returns></returns>
public MemoryStream GetSerializedDataStream()
{
if (!_serialized)
{
Serialize();
}
return _serializedData;
}
/// <summary>
/// Returns the serialized game state data as a byte array
/// </summary>
/// <returns></returns>
public byte[] GetSerializedDataBuffer()
{
if (!_serialized)
{
Serialize();
}
return _serializedData.ToArray();
}
/// <summary>
/// Deserializes a game state from a byte array and returns it
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static GameState Deserialize(byte[] data)
{
var serializer = IoCManager.Resolve<ISS14Serializer>();
using (var stream = new MemoryStream(data))
{
return serializer.Deserialize<GameState>(new MemoryStream(data));
}
}
public readonly List<EntityState> EntityStates;
public readonly List<PlayerState> PlayerStates;
public readonly List<EntityUid> EntityDeletions;
}
}

View File

@@ -1,54 +0,0 @@
using System;
using System.IO;
using SS14.Shared.Bsdiff;
using SS14.Shared.GameStates;
using SS14.Shared.Interfaces.Serialization;
using SS14.Shared.IoC;
using SS14.Shared.Serialization;
namespace SS14.Shared.GameStates
{
[Serializable, NetSerializable]
public class GameStateDelta
{
public byte[] deltaBytes;
public uint FromSequence { get; set; }
public uint Sequence { get; set; }
public GameStateDelta(byte[] bytes)
{
deltaBytes = bytes;
}
public GameStateDelta()
{
}
public long Size
{
get { return deltaBytes.Length; }
}
public void Create(GameState fromState, GameState toState)
{
Sequence = toState.Sequence;
FromSequence = fromState.Sequence;
using (var stream = Bsdiff.Bsdiff.GenerateBzip2Diff(fromState.GetSerializedDataBuffer(), toState.GetSerializedDataBuffer()))
{
deltaBytes = stream.ToArray();
}
}
public GameState Apply(GameState fromState)
{
if (fromState.Sequence != FromSequence)
throw new Exception("Cannot apply GameStateDelta. Sequence incorrect.");
byte[] fromBuffer = fromState.GetSerializedDataStream().ToArray();
using (var newBytes = Bsdiff.Bsdiff.ApplyBzip2Patch(fromBuffer, deltaBytes))
{
var serializer = IoCManager.Resolve<ISS14Serializer>();
return serializer.Deserialize<GameState>(newBytes);
}
}
}
}

View File

@@ -58,6 +58,10 @@ namespace SS14.Shared.Interfaces.GameObjects
/// </summary>
bool Deleted { get; }
void Dirty();
uint LastModifiedTick { get; }
/// <summary>
/// Called when the component is removed from an entity.
/// Shuts down the component.

View File

@@ -9,6 +9,8 @@ namespace SS14.Shared.Interfaces.GameObjects
{
public interface IEntity
{
uint LastModifiedTick { get; }
IEntityManager EntityManager { get; }
/// <summary>
@@ -202,10 +204,12 @@ namespace SS14.Shared.Interfaces.GameObjects
/// Serverside method to prepare an entity state object
/// </summary>
/// <returns></returns>
EntityState GetEntityState();
EntityState GetEntityState(uint fromTick);
void SubscribeEvent<T>(EntityEventHandler<EntityEventArgs> evh, IEntityEventSubscriber s) where T : EntityEventArgs;
void UnsubscribeEvent<T>(IEntityEventSubscriber s) where T : EntityEventArgs;
void RaiseEvent(EntityEventArgs toRaise);
void Dirty();
}
}

View File

@@ -7,6 +7,8 @@ namespace SS14.Shared.Interfaces.GameObjects
{
public interface IEntityManager
{
uint CurrentTick { get; }
void Initialize();
void Startup();
void Shutdown();

View File

@@ -120,7 +120,7 @@ namespace SS14.Shared.Interfaces.Network
/// <typeparam name="T">Type to register.</typeparam>
/// <param name="name">String ID of the message.</param>
/// <param name="rxCallback">Callback function to process the received message.</param>
void RegisterNetMessage<T>(string name, ProcessMessage rxCallback = null)
void RegisterNetMessage<T>(string name, ProcessMessage<T> rxCallback = null)
where T : NetMessage;
/// <summary>

View File

@@ -24,7 +24,7 @@ namespace SS14.Shared.Map
/// <inheritdoc />
public void Initialize()
{
_netManager.RegisterNetMessage<MsgMap>(MsgMap.NAME, message => HandleNetworkMessage((MsgMap)message));
_netManager.RegisterNetMessage<MsgMap>(MsgMap.NAME, HandleNetworkMessage);
_netManager.RegisterNetMessage<MsgMapReq>(MsgMapReq.NAME, message => SendMap(message.MsgChannel));
}

View File

@@ -1,91 +0,0 @@
using ICSharpCode.SharpZipLib.GZip;
using Lidgren.Network;
using SS14.Shared.GameStates;
using SS14.Shared.Interfaces.Network;
using SS14.Shared.Interfaces.Serialization;
using SS14.Shared.IoC;
using System.IO;
namespace SS14.Shared.Network.Messages
{
public class MsgFullState : NetMessage
{
#region REQUIRED
public static readonly MsgGroups GROUP = MsgGroups.Entity;
public static readonly string NAME = nameof(MsgFullState);
public MsgFullState(INetChannel channel) : base(NAME, GROUP) { }
#endregion
public GameState State { get; set; }
public override void ReadFromBuffer(NetIncomingMessage buffer)
{
int length = buffer.ReadInt32();
byte[] stateData = Decompress(buffer.ReadBytes(length));
using (var stateStream = new MemoryStream(stateData))
{
var serializer = IoCManager.Resolve<ISS14Serializer>();
State = serializer.Deserialize<GameState>(stateStream);
}
}
public override void WriteToBuffer(NetOutgoingMessage buffer)
{
byte[] stateData = Compress(State.GetSerializedDataBuffer());
buffer.Write(stateData.Length);
buffer.Write(stateData);
}
#region Compression
/// <summary>
/// Compresses a decompressed state data byte array into a compressed one.
/// </summary>
/// <param name="stateData">full state data</param>
/// <returns></returns>
private static byte[] Compress(byte[] stateData)
{
using (var compressedDataStream = new MemoryStream())
{
using (var gzip = new GZipOutputStream(compressedDataStream))
{
gzip.Write(stateData, 0, stateData.Length);
}
return compressedDataStream.ToArray();
}
}
/// <summary>
/// Decompresses a compressed state data byte array into a decompressed one.
/// </summary>
/// <param name="compressedStateData">compressed state data</param>
/// <returns></returns>
private static byte[] Decompress(byte[] compressedStateData)
{
// Create a GZIP stream with decompression mode.
// ... Then create a buffer and write into while reading from the GZIP stream.
using (var compressedStream = new MemoryStream(compressedStateData))
using (var stream = new GZipInputStream(compressedStream))
{
const int size = 2048;
var buffer = new byte[size];
using (var memory = new MemoryStream())
{
int count = 0;
do
{
count = stream.Read(buffer, 0, size);
if (count > 0)
{
memory.Write(buffer, 0, count);
}
} while (count > 0);
return memory.ToArray();
}
}
}
#endregion Compression
}
}

View File

@@ -0,0 +1,43 @@
using ICSharpCode.SharpZipLib.GZip;
using Lidgren.Network;
using SS14.Shared.GameStates;
using SS14.Shared.Interfaces.Network;
using SS14.Shared.Interfaces.Serialization;
using SS14.Shared.IoC;
using System.IO;
namespace SS14.Shared.Network.Messages
{
public class MsgState : NetMessage
{
#region REQUIRED
public static readonly MsgGroups GROUP = MsgGroups.Entity;
public static readonly string NAME = nameof(MsgState);
public MsgState(INetChannel channel) : base(NAME, GROUP) { }
#endregion
public GameState State { get; set; }
public override void ReadFromBuffer(NetIncomingMessage buffer)
{
var length = buffer.ReadInt32();
var stateData = buffer.ReadBytes(length);
using (var stateStream = new MemoryStream(stateData))
{
var serializer = IoCManager.Resolve<ISS14Serializer>();
State = serializer.Deserialize<GameState>(stateStream);
}
}
public override void WriteToBuffer(NetOutgoingMessage buffer)
{
var serializer = IoCManager.Resolve<ISS14Serializer>();
using (var stateStream = new MemoryStream())
{
serializer.Serialize(stateStream, State);
buffer.Write((int)stateStream.Length);
buffer.Write(stateStream.ToArray());
}
}
}
}

View File

@@ -1,38 +0,0 @@
using Lidgren.Network;
using SS14.Shared.GameStates;
using SS14.Shared.Interfaces.Network;
namespace SS14.Shared.Network.Messages
{
public class MsgStateUpdate : NetMessage
{
#region REQUIRED
public static readonly MsgGroups GROUP = MsgGroups.Entity;
public static readonly string NAME = nameof(MsgStateUpdate);
public MsgStateUpdate(INetChannel channel) : base(NAME, GROUP) { }
#endregion
public GameStateDelta StateDelta { get; set; }
public override void ReadFromBuffer(NetIncomingMessage buffer)
{
uint sequence = buffer.ReadUInt32();
uint fromSequence = buffer.ReadUInt32();
int length = buffer.ReadInt32();
byte[] bytes;
buffer.ReadBytes(length, out bytes);
StateDelta = new GameStateDelta(bytes);
StateDelta.Sequence = sequence;
StateDelta.FromSequence = fromSequence;
}
public override void WriteToBuffer(NetOutgoingMessage buffer)
{
buffer.Write(StateDelta.Sequence);
buffer.Write(StateDelta.FromSequence);
buffer.Write(StateDelta.deltaBytes.Length);
buffer.Write(StateDelta.deltaBytes);
}
}
}

View File

@@ -17,6 +17,12 @@ namespace SS14.Shared.Network
/// <param name="message">The message received.</param>
public delegate void ProcessMessage(NetMessage message);
/// <summary>
/// Callback for registered NetMessages.
/// </summary>
/// <param name="message">The message received.</param>
public delegate void ProcessMessage<T>(T message) where T : NetMessage;
/// <summary>
/// Manages all network connections and packet IO.
/// </summary>
@@ -403,7 +409,7 @@ namespace SS14.Shared.Network
#region NetMessages
/// <inheritdoc />
public void RegisterNetMessage<T>(string name, ProcessMessage rxCallback = null)
public void RegisterNetMessage<T>(string name, ProcessMessage<T> rxCallback = null)
where T : NetMessage
{
_strings.AddString(name);
@@ -411,7 +417,7 @@ namespace SS14.Shared.Network
_messages.Add(name, typeof(T));
if (rxCallback != null)
_callbacks.Add(typeof(T), rxCallback);
_callbacks.Add(typeof(T), msg => rxCallback((T)msg));
}
/// <inheritdoc />

View File

@@ -55,9 +55,7 @@ namespace SS14.Shared.Network
if (_network.IsServer) // Server does not receive entries from clients.
return;
var msg = (MsgStringTableEntries)message;
foreach (var entry in msg.Entries)
foreach (var entry in message.Entries)
{
var id = entry.Id;
var str = string.IsNullOrEmpty(entry.String) ? null : entry.String;
@@ -70,7 +68,7 @@ namespace SS14.Shared.Network
{
if (TryFindStringId(str, out int oldId))
{
if(oldId == id)
if (oldId == id)
continue;
_strings.Remove(oldId);
@@ -211,7 +209,7 @@ namespace SS14.Shared.Network
if (_network.IsClient)
return;
if(!_network.IsRunning)
if (!_network.IsRunning)
return;
var message = _network.CreateNetMessage<MsgStringTableEntries>();
@@ -260,7 +258,7 @@ namespace SS14.Shared.Network
public static readonly string NAME = nameof(MsgStringTableEntries);
public MsgStringTableEntries(INetChannel channel) : base(NAME, GROUP) { }
#endregion
public Entry[] Entries { get; set; }
/// <summary>
@@ -295,7 +293,7 @@ namespace SS14.Shared.Network
/// <inheritdoc />
public override void WriteToBuffer(NetOutgoingMessage buffer)
{
if(Entries == null)
if (Entries == null)
throw new InvalidOperationException("Entries is null!");
buffer.Write(Entries.Length);

View File

@@ -133,10 +133,6 @@
<Project>{59250BAF-0000-0000-0000-000000000000}</Project>
<Private>True</Private>
</ProjectReference>
<ProjectReference Include="..\SS14.Shared.Bsdiff\SS14.Shared.Bsdiff\SS14.Shared.Bsdiff.csproj">
<Project>{0e0723e8-d785-4f61-81f4-182a12bcaa52}</Project>
<Name>SS14.Shared.Bsdiff</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Audio\AudioParams.cs" />
@@ -204,7 +200,6 @@
<Compile Include="Interfaces\Map\ITileDefinitionManager.cs" />
<Compile Include="Interfaces\Timers\ITimerManager.cs" />
<Compile Include="Interfaces\Timing\IGameTiming.cs" />
<Compile Include="GameStates\GameStateDelta.cs" />
<Compile Include="GameStates\GameState.cs" />
<Compile Include="GameStates\PlayerState.cs" />
<Compile Include="IoC\Exceptions\ImplementationConstructorException.cs" />
@@ -247,8 +242,7 @@
<Compile Include="Maths\Vector2u.cs" />
<Compile Include="Maths\Vector3.cs" />
<Compile Include="Maths\Vector4.cs" />
<Compile Include="Network\Messages\MsgFullState.cs" />
<Compile Include="Network\Messages\MsgStateUpdate.cs" />
<Compile Include="Network\Messages\MsgState.cs" />
<Compile Include="Network\NetChannelArgs.cs" />
<Compile Include="Network\NetMessageArgs.cs" />
<Compile Include="Network\Messages\MsgChat.cs" />

View File

@@ -75,6 +75,8 @@ using SS14.Client.Interfaces.Graphics;
using SS14.Client.Graphics.Lighting;
using SS14.Shared.Log;
using SS14.Server.Prototypes;
using SS14.Client.Interfaces.GameStates;
using SS14.Client.GameStates;
namespace SS14.UnitTesting
{
@@ -206,7 +208,7 @@ namespace SS14.UnitTesting
IoCManager.Register<IClientNetManager, NetManager>();
IoCManager.Register<IClientEntityManager, ClientEntityManager>();
IoCManager.Register<IEntityNetworkManager, ClientEntityNetworkManager>();
IoCManager.Register<SS14.Client.Interfaces.GameStates.IGameStateManager, SS14.Client.GameStates.GameStateManager>();
IoCManager.Register<IClientGameStateManager, ClientGameStateManager>();
IoCManager.Register<IBaseClient, BaseClient>();
IoCManager.Register<SS14.Client.Interfaces.Player.IPlayerManager, SS14.Client.Player.PlayerManager>();
IoCManager.Register<IStateManager, StateManager>();
@@ -234,7 +236,7 @@ namespace SS14.UnitTesting
IoCManager.Register<ITileDefinitionManager, TileDefinitionManager>();
IoCManager.Register<IEntityNetworkManager, ServerEntityNetworkManager>();
IoCManager.Register<ICommandLineArgs, CommandLineArgs>();
IoCManager.Register<IGameStateManager, GameStateManager>();
IoCManager.Register<IServerGameStateManager, ServerGameStateManager>();
IoCManager.Register<IReflectionManager, ServerReflectionManager>();
IoCManager.Register<IClientConsoleHost, SS14.Server.ClientConsoleHost.ClientConsoleHost>();
IoCManager.Register<IPlayerManager, SS14.Server.Player.PlayerManager>();

View File

@@ -18,8 +18,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SS14.Client", "SS14.Client\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SS14.Client.Godot", "SS14.Client.Godot\SS14.Client.Godot.csproj", "{8AF31169-49B1-4A12-B8F4-2A0674A9E7CB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SS14.Shared.Bsdiff", "SS14.Shared.Bsdiff\SS14.Shared.Bsdiff\SS14.Shared.Bsdiff.csproj", "{0E0723E8-D785-4F61-81F4-182A12BCAA52}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sandbox", "Sandbox", "{C0BDFCEF-B85A-4251-9B32-F676E28E34B5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sandbox.Client", "Sandbox.Client\Sandbox.Client.csproj", "{BC6CD011-C070-4382-97AA-EA90959FBF85}"
@@ -167,24 +165,6 @@ Global
{8AF31169-49B1-4A12-B8F4-2A0674A9E7CB}.Tools|x64.Build.0 = Tools|Any CPU
{8AF31169-49B1-4A12-B8F4-2A0674A9E7CB}.Tools|x86.ActiveCfg = Tools|Any CPU
{8AF31169-49B1-4A12-B8F4-2A0674A9E7CB}.Tools|x86.Build.0 = Tools|Any CPU
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Debug|x64.ActiveCfg = Debug|x64
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Debug|x64.Build.0 = Debug|x64
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Debug|x86.ActiveCfg = Debug|x86
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Debug|x86.Build.0 = Debug|x86
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Release|Any CPU.Build.0 = Release|Any CPU
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Release|x64.ActiveCfg = Release|x64
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Release|x64.Build.0 = Release|x64
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Release|x86.ActiveCfg = Release|x86
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Release|x86.Build.0 = Release|x86
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Tools|Any CPU.ActiveCfg = Debug|Any CPU
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Tools|Any CPU.Build.0 = Debug|Any CPU
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Tools|x64.ActiveCfg = Debug|x64
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Tools|x64.Build.0 = Debug|x64
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Tools|x86.ActiveCfg = Debug|x86
{0E0723E8-D785-4F61-81F4-182A12BCAA52}.Tools|x86.Build.0 = Debug|x86
{BC6CD011-C070-4382-97AA-EA90959FBF85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BC6CD011-C070-4382-97AA-EA90959FBF85}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC6CD011-C070-4382-97AA-EA90959FBF85}.Debug|x64.ActiveCfg = Debug|x64

View File

@@ -1,76 +0,0 @@
#!/usr/bin/env python3
import os
import sys
import urllib.request
import shutil
CURRENT_VERSION = "0.3.1"
RELEASES_ROOT = "https://github.com/space-wizards/ss14.shared.bsdiff/releases/download/" \
+ CURRENT_VERSION + "/"
WINDOWS_FILENAME = "bsdiffwrap-x86_64-pc-windows-msvc.dll"
MACOS_FILENAME = "libbsdiffwrap-x86_64-apple-darwin.dylib"
LINUX_FILENAME = "libbsdiffwrap-x86_64-unknown-linux-gnu.so"
WINDOWS_TARGET_FILENAME = "bsdiffwrap.dll"
LINUX_TARGET_FILENAME = "libbsdiffwrap.so"
MACOS_TARGET_FILENAME = "libbsdiffwrap.dylib"
def main():
platform = sys.argv[1]
target_os = sys.argv[2]
# Hah good luck passing something containing a space to the Exec MSBuild Task.
target_dir = " ".join(sys.argv[3:])
if platform != "x64":
print("Error: Unable to download bsdiffwrap for any platform outside x64. "
"If you REALLY want x86 support for some misguided reason, I'm not providing it.")
exit(1)
repo_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
dependencies_dir = os.path.join(repo_dir, "Dependencies", "bsdiffwrap")
version_file = os.path.join(dependencies_dir, "VERSION")
os.makedirs(dependencies_dir, exist_ok=True)
existing_version = "?"
if os.path.exists(version_file):
with open(version_file, "r") as f:
existing_version = f.read().strip()
if existing_version != CURRENT_VERSION:
for x in os.listdir(dependencies_dir):
os.remove(x)
with open(version_file, "w") as f:
f.write(CURRENT_VERSION)
filename = None
target_filename = None
if target_os == "Windows":
filename = WINDOWS_FILENAME
target_filename = WINDOWS_TARGET_FILENAME
elif target_os == "Linux":
filename = LINUX_FILENAME
target_filename = LINUX_TARGET_FILENAME
elif target_os == "MacOS":
filename = MACOS_FILENAME
target_filename = MACOS_TARGET_FILENAME
else:
print("Error: Unknown platform target:", target_os)
exit(2)
dependency_path = os.path.join(dependencies_dir, filename)
if not os.path.exists(dependency_path):
urllib.request.urlretrieve(RELEASES_ROOT + filename, dependency_path)
target_file_path = os.path.join(target_dir, target_filename)
if not os.path.exists(target_file_path) or \
os.stat(dependency_path).st_mtime > os.stat(target_file_path).st_mtime:
shutil.copy2(dependency_path, target_file_path)
if __name__ == '__main__':
main()