Skip to content

Commit

Permalink
Save session data on logout, add spawn points (#40)
Browse files Browse the repository at this point in the history
* Save session data on logout, add spawn points

* Fix warning about namespaces order

* Fix remaining warnings

* Move HostileFactionIds
  • Loading branch information
SzymonKaminski committed Mar 28, 2024
1 parent 9a709e4 commit 874dfa9
Show file tree
Hide file tree
Showing 17 changed files with 223 additions and 31 deletions.
9 changes: 5 additions & 4 deletions UdpHosts/GameServer/Controllers/Character/BaseController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using AeroMessages.GSS.V66.Character.View;
using GameServer.Data;
using GameServer.Data.SDB;
using GameServer.Data.SDB.Records.customdata;
using GameServer.Entities.Character;
using GameServer.Entities.Vehicle;
using GameServer.Enums.GSS.Character;
Expand Down Expand Up @@ -329,29 +330,29 @@ public void RequestTeleport(INetworkClient client, IPlayer player, ulong entityI
var query = packet.Unpack<RequestTeleport>();

// TODO: Raycast for z
var targetPosition = new Vector3(query.PosX, query.PosY, 500);
var spawnPoint = new SpawnPoint { Position = new Vector3(query.PosX, query.PosY, 500) };

// Workaround by looking for Z coordinate in Outposts since that's the primary way players send this message
var zoneId = player.CurrentZone.ID;
foreach (var outpost in CustomDBInterface.GetZoneOutposts(zoneId).Values)
{
if (Math.Round(outpost.Position.X) == Math.Round(query.PosX) && Math.Round(outpost.Position.Y) == Math.Round(query.PosY))
{
targetPosition.Z = outpost.Position.Z;
spawnPoint = client.AssignedShard.Outposts[zoneId][outpost.Id].RandomSpawnPoint;
break;
}
}

// Instantly transport character to target location
character.SetPosition(targetPosition);
character.PositionAtSpawnPoint(spawnPoint);
var forcedMove = new ForcedMovement
{
Data = new ForcedMovementData
{
Type = 1,
Unk1 = 0,
HaveUnk2 = 0,
Params1 = new ForcedMovementType1Params { Position = targetPosition, Direction = character.AimDirection, Velocity = Vector3.Zero, Time = character.Shard.CurrentTime + 1 }
Params1 = new ForcedMovementType1Params { Position = spawnPoint.Position, Direction = character.AimDirection, Velocity = Vector3.Zero, Time = character.Shard.CurrentTime + 1 }
},
ShortTime = character.Shard.CurrentShortTime
};
Expand Down
34 changes: 34 additions & 0 deletions UdpHosts/GameServer/Controllers/GenericShard.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System;
using System.Linq;
using System.Numerics;
using AeroMessages.Control;
using AeroMessages.GSS.V66.Generic;
using GameServer.Aptitude;
using GameServer.Entities;
using GameServer.Enums;
using GameServer.Enums.GSS.Generic;
using GameServer.Extensions;
using GameServer.GRPC;
using GameServer.Packets;
using Serilog;

Expand Down Expand Up @@ -91,6 +93,38 @@ public void RequestLogout(INetworkClient client, IPlayer player, ulong entityId,
{
var resp = new CloseConnection { Unk = new byte[] { 0, 0, 0, 0 } };
client.NetChannels[ChannelType.Control].SendIAero(resp);

var zone = player.CurrentZone;

if (!zone.IsOpenWorld)
{
return;
}

var playerPosition = player.CharacterEntity.Position;

var minDistance = Vector3.DistanceSquared(playerPosition, zone.POIs["spawn"]);
var closestOutpostId = zone.DefaultOutpostId;

if (client.AssignedShard.Outposts.TryGetValue(zone.ID, out var outposts))
{
foreach (var outpost in outposts)
{
var distance = Vector3.DistanceSquared(playerPosition, outpost.Value.Outpost_ObserverView.PositionProp);

if (distance < minDistance)
{
minDistance = distance;
closestOutpostId = outpost.Key;
}
}
}

_ = GRPCService.SaveCharacterSessionDataAsync(
player.CharacterId + 0xFE,
zone.ID,
closestOutpostId,
(uint)DateTimeOffset.UtcNow.ToUnixTimeSeconds() - player.ConnectedAt);
}

[MessageID((byte)Commands.RequestEncounterInfo)]
Expand Down
2 changes: 2 additions & 0 deletions UdpHosts/GameServer/Data/HardcodedCharacterData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public static class HardcodedCharacterData
public static byte EffectiveLevel = 45;
public static uint MaxHealth = 19192;
public static uint GeneratedLoadoutCounter = 20001;
public static HashSet<uint> HostileFactionIds = new() { 2, 3, 5, 6, 7, 8, 17, 22, 42, 43, 45, 46, 47, 48 };

public static BasicCharacterData FallbackData = new BasicCharacterData()
{
Expand Down Expand Up @@ -957,6 +958,7 @@ public class BasicCharacterInfo
public string ArmyTag { get; set; }
public ulong ArmyGuid { get; set; }
public bool ArmyIsOfficer { get; set; }
public uint TimePlayed { get; set; }
}

public class BasicCharacterVisuals
Expand Down
4 changes: 4 additions & 0 deletions UdpHosts/GameServer/Data/Zone.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace GameServer.Data;

public class Zone
{
private static readonly HashSet<uint> OpenWorldZones = new() { 162, 448, 1030 };

public Zone()
{
POIs = new Dictionary<string, Vector3>();
Expand All @@ -14,4 +16,6 @@ public Zone()
public string Name { get; set; }
public ulong Timestamp { get; set; }
public Dictionary<string, Vector3> POIs { get; protected set; }
public uint DefaultOutpostId { get; set; }
public bool IsOpenWorld => OpenWorldZones.Contains(ID);
}
23 changes: 22 additions & 1 deletion UdpHosts/GameServer/Entities/Character/CharacterEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using GameServer.Controllers;
using GameServer.Data;
using GameServer.Data.SDB;
using GameServer.Data.SDB.Records.customdata;
using GameServer.Data.SDB.Records.dbstats;
using GameServer.Enums;
using GameServer.Test;
Expand Down Expand Up @@ -103,6 +104,7 @@ public CharacterEntity(IShard shard, ulong eid)
public ulong ArmyGUID { get; set; }
public byte ArmyIsOfficer { get; set; }
public CharacterStateData CharacterState { get; set; }
public uint TimePlayed { get; set; }
public HostilityInfoData HostilityInfo { get; set; }
public MaxVital MaxShields { get; set; }
public MaxVital MaxHealth { get; set; }
Expand Down Expand Up @@ -378,6 +380,7 @@ public void LoadRemote(CharacterAndBattleframeVisuals remoteData)
ArmyGuid = remoteData.CharacterInfo.ArmyGuid,
ArmyTag = remoteData.CharacterInfo.ArmyTag,
ArmyIsOfficer = remoteData.CharacterInfo.ArmyIsOfficer,
TimePlayed = remoteData.CharacterInfo.TimePlayed,
},
CharacterVisuals = new Data.BasicCharacterVisuals()
{
Expand Down Expand Up @@ -448,6 +451,7 @@ public void Load(BasicCharacterData data)
ArmyTag = DataUtils.FormatArmyTag(info.ArmyTag)
});

SetTimePlayed(info.TimePlayed);
SetArmyGUID(info.ArmyGuid);
SetArmyIsOfficer((byte)(info.ArmyIsOfficer ? 1 : 0));
}
Expand Down Expand Up @@ -703,6 +707,15 @@ public void SetStaticInfo(StaticInfoData value)
}
}

public void SetTimePlayed(uint value)
{
TimePlayed = value;
if (Character_BaseController != null)
{
Character_BaseController.TimePlayedProp = TimePlayed;
}
}

public void SetArmyGUID(ulong value)
{
ArmyGUID = value;
Expand Down Expand Up @@ -846,6 +859,14 @@ public void SetRotation(Quaternion newRotation)
RefreshMovementView();
}

public void PositionAtSpawnPoint(SpawnPoint spawnPoint)
{
Position = spawnPoint.Position;
Rotation = spawnPoint.Orientation;
AimDirection = spawnPoint.AimDirection;
RefreshMovementView();
}

public void SetSpawnPose()
{
SpawnPose = new CharacterSpawnPose
Expand Down Expand Up @@ -1097,7 +1118,7 @@ private void InitControllers()
{
Character_BaseController = new BaseController
{
TimePlayedProp = 0,
TimePlayedProp = TimePlayed,
CurrentWeightProp = 0,
EncumberedWeightProp = 255,
AuthorizedTerminalProp = AuthorizedTerminal,
Expand Down
23 changes: 22 additions & 1 deletion UdpHosts/GameServer/Entities/Outpost/OutpostEntity.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
using System;
using System.Numerics;
using System.Collections.Generic;
using AeroMessages.Common;
using AeroMessages.GSS.V66;
using AeroMessages.GSS.V66.Outpost.View;
using GameServer.Data;
using GameServer.Data.SDB.Records.customdata;

namespace GameServer.Entities.Outpost;

public class OutpostEntity : BaseEntity
{
private static readonly Random Rng = new();

public OutpostEntity(IShard shard, ulong eid, Data.SDB.Records.customdata.Outpost record)
: base(shard, eid)
{
AeroEntityId = new EntityId() { Backing = EntityId, ControllerId = Controller.Outpost };
Scoping = new ScopingComponent() { Global = true };
InitFields();
InitViews(record);
AddSpawnPoints(record);
}

public ObserverView Outpost_ObserverView { get; set; }

public ulong EncounterId { get; set; }
public ScopeBubbleInfoData ScopeBubbleInfo { get; set; }

public SpawnPoint RandomSpawnPoint => SpawnPoints[Rng.Next(SpawnPoints.Count)];
public bool IsCapturedByHostiles => HardcodedCharacterData.HostileFactionIds.Contains(Outpost_ObserverView.FactionIdProp);
private List<SpawnPoint> SpawnPoints { get; set; } = new();

private void InitFields()
{
ScopeBubbleInfo = new ScopeBubbleInfoData()
Expand Down Expand Up @@ -56,4 +65,16 @@ private void InitViews(Data.SDB.Records.customdata.Outpost record)
ScopeBubbleInfoProp = ScopeBubbleInfo
};
}

private void AddSpawnPoints(Data.SDB.Records.customdata.Outpost record)
{
if (record.SpawnPoints.Count == 0)
{
SpawnPoints = new List<SpawnPoint> { new() { Position = record.Position } };
}
else
{
SpawnPoints = record.SpawnPoints;
}
}
}
13 changes: 12 additions & 1 deletion UdpHosts/GameServer/GRPC/GRPCService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,20 @@ public static async Task<CharacterAndBattleframeVisuals> GetCharacterAndBattlefr
return await _client.GetCharacterAndBattleframeVisualsAsync(new CharacterID { ID = characterId });
}

public static async Task<Empty> SaveCharacterSessionDataAsync(ulong characterId, uint zoneId, uint outpostId, uint timePlayed)
{
return await _client.SaveCharacterGameSessionDataAsync(new GameSessionData()
{
CharacterId = characterId,
ZoneId = zoneId,
OutpostId = outpostId,
TimePlayed = timePlayed
});
}

public static async Task ListenAsync(ConcurrentDictionary<uint, INetworkPlayer> clientMap)
{
using var listen = _client.Listen(new EmptyReq());
using var listen = _client.Listen(new Empty());

var reader =
Task.Run(async () =>
Expand Down
14 changes: 12 additions & 2 deletions UdpHosts/GameServer/GRPC/GameServerAPI.proto
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ message BasicCharacterInfo {
string ArmyTag = 7;
uint64 ArmyGuid = 8;
bool ArmyIsOfficer = 9;
uint32 LastZoneId = 10;
uint32 LastOutpostId = 11;
uint32 TimePlayed = 12;
}
message CharacterAndBattleframeVisuals {
BasicCharacterInfo CharacterInfo = 1;
Expand Down Expand Up @@ -95,7 +98,7 @@ message CharacterVisualsUpdated {
uint64 CharacterGuid = 1;
CharacterAndBattleframeVisuals CharacterAndBattleframeVisuals = 2;
}
message EmptyReq {
message Empty {
}
message Event {
oneof subtype {
Expand All @@ -114,6 +117,12 @@ message Event {
CharacterVisualsUpdated CharacterVisualsUpdated = 13;
}
}
message GameSessionData {
uint64 CharacterId = 1;
uint32 ZoneId = 2;
uint32 OutpostId = 3;
uint32 TimePlayed = 4;
}
message PingReq {
.google.protobuf.Timestamp SentTime = 1;
}
Expand Down Expand Up @@ -154,6 +163,7 @@ message WebIdValueColorId {
}
service GameServerAPI {
rpc GetCharacterAndBattleframeVisuals (CharacterID) returns (CharacterAndBattleframeVisuals);
rpc Listen (EmptyReq) returns (stream Event);
rpc Listen (Empty) returns (stream Event);
rpc Ping (PingReq) returns (PingResp);
rpc SaveCharacterGameSessionData (GameSessionData) returns (Empty);
}
5 changes: 1 addition & 4 deletions UdpHosts/GameServer/GameServer.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
using System;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using FauFau.Formats;
using GameServer.Controllers;
using GameServer.Data.SDB;
using GameServer.GRPC;
using GameServer.Test;
using Grpc.Net.Client;
using GrpcGameServerAPIClient;
using Serilog;
using Shared.Udp;
using System.Threading.Tasks;

namespace GameServer;

Expand Down
2 changes: 2 additions & 0 deletions UdpHosts/GameServer/IPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum PlayerStatus
CharacterInventory Inventory { get; set; }
PlayerStatus Status { get; }
Zone CurrentZone { get; }
uint ConnectedAt { get; }
uint LastRequestedUpdate { get; set; }
uint RequestedClientTime { get; set; }
bool FirstUpdateRequested { get; set; }
Expand All @@ -49,4 +50,5 @@ public enum PlayerStatus
void Respawn();
void Jump();
void Tick(double deltaTime, ulong currentTime, CancellationToken ct);
uint FindClosestAvailableOutpost(Zone zone, uint targetOutpostId);
}
2 changes: 2 additions & 0 deletions UdpHosts/GameServer/IShard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading;
using GameServer.Aptitude;
using GameServer.Entities;
using GameServer.Entities.Outpost;
using Shared.Udp;

namespace GameServer;
Expand All @@ -12,6 +13,7 @@ public interface IShard : IPacketSender
ulong InstanceId { get; }
IDictionary<uint, INetworkPlayer> Clients { get; }
IDictionary<ulong, IEntity> Entities { get; }
IDictionary<uint, IDictionary<uint, OutpostEntity>> Outposts { get; }
PhysicsEngine Physics { get; }
AIEngine AI { get; }
MovementRelay Movement { get; }
Expand Down
Loading

0 comments on commit 874dfa9

Please sign in to comment.