1056 lines
26 KiB
C#
1056 lines
26 KiB
C#
using System;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using Newtonsoft.Json.Converters;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace GeoSus.Client
|
|
{
|
|
#region Základní typy
|
|
|
|
public struct Position
|
|
{
|
|
[JsonProperty("lat")]
|
|
public double Lat { get; set; }
|
|
|
|
[JsonProperty("lon")]
|
|
public double Lon { get; set; }
|
|
|
|
public Position(double lat, double lon)
|
|
{
|
|
Lat = lat;
|
|
Lon = lon;
|
|
}
|
|
|
|
// Haversine vzdálenost v metrech
|
|
public double DistanceTo(Position other)
|
|
{
|
|
const double R = 6371000;
|
|
var lat1 = Lat * Math.PI / 180;
|
|
var lat2 = other.Lat * Math.PI / 180;
|
|
var dLat = (other.Lat - Lat) * Math.PI / 180;
|
|
var dLon = (other.Lon - Lon) * Math.PI / 180;
|
|
|
|
var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
|
|
Math.Cos(lat1) * Math.Cos(lat2) *
|
|
Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
|
|
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
|
|
|
|
return R * c;
|
|
}
|
|
public static bool operator ==(Position left, Position right) { if (left.Lat == right.Lat && left.Lon == right.Lon) { return true; } else { return false; } }
|
|
public static bool operator !=(Position left, Position right) { return !(left == right); }
|
|
}
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public enum PlayerRole { Crew, Impostor }
|
|
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public enum PlayerState { Alive, Dead }
|
|
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public enum GamePhase { Lobby, Loading, Playing, Meeting, Voting, Ended }
|
|
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public enum TaskType { Instant }
|
|
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public enum MeetingType { BodyReport, Emergency }
|
|
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public enum SabotageType { CommsBlackout, CriticalMeltdown }
|
|
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public enum SabotageState { Inactive, Active, Repaired }
|
|
|
|
// Map data types for Overpass integration
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public enum PathType
|
|
{
|
|
Footway,
|
|
Path,
|
|
Steps,
|
|
Cycleway,
|
|
Pedestrian,
|
|
Road,
|
|
Service,
|
|
Residential,
|
|
Track,
|
|
Other
|
|
}
|
|
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public enum MapAreaType
|
|
{
|
|
Park,
|
|
Garden,
|
|
Playground,
|
|
Forest,
|
|
Grass,
|
|
Water,
|
|
Other
|
|
}
|
|
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public enum MapPOIType
|
|
{
|
|
FoodDrink, // Restaurants, cafes, bars
|
|
Shop, // Shops
|
|
Health, // Pharmacies, hospitals
|
|
Transport, // Bus stops, parking
|
|
Culture, // Museums, theaters
|
|
Landmark, // Churches, monuments
|
|
Recreation, // Parks, playgrounds
|
|
Other
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Zprávy
|
|
|
|
public abstract class Message
|
|
{
|
|
[JsonProperty("type")]
|
|
public abstract string Type { get; }
|
|
|
|
[JsonProperty("clientSeq")]
|
|
public int ClientSeq { get; set; }
|
|
|
|
[JsonProperty("actionId")]
|
|
public string? ActionId { get; set; }
|
|
}
|
|
|
|
// Handshake
|
|
public class ClientHello : Message
|
|
{
|
|
public override string Type => "ClientHello";
|
|
|
|
[JsonProperty("protocolVersion")]
|
|
public string ProtocolVersion { get; set; } = "1.0";
|
|
|
|
[JsonProperty("clientUuid")]
|
|
public string ClientUuid { get; set; } = "";
|
|
|
|
[JsonProperty("displayName")]
|
|
public string? DisplayName { get; set; }
|
|
}
|
|
|
|
public class ServerHello : Message
|
|
{
|
|
public override string Type => "ServerHello";
|
|
|
|
[JsonProperty("rsaPublicKeyPem")]
|
|
public string RsaPublicKeyPem { get; set; } = "";
|
|
|
|
[JsonProperty("serverId")]
|
|
public string ServerId { get; set; } = "";
|
|
}
|
|
|
|
public class KeyExchange : Message
|
|
{
|
|
public override string Type => "KeyExchange";
|
|
|
|
[JsonProperty("encryptedSessionKey")]
|
|
public string EncryptedSessionKey { get; set; } = "";
|
|
|
|
[JsonProperty("encryptedIV")]
|
|
public string EncryptedIV { get; set; } = "";
|
|
}
|
|
|
|
public class KeyExchangeAck : Message
|
|
{
|
|
public override string Type => "KeyExchangeAck";
|
|
|
|
[JsonProperty("status")]
|
|
public string Status { get; set; } = "";
|
|
}
|
|
|
|
// Lobby
|
|
public class CreateLobby : Message
|
|
{
|
|
public override string Type => "CreateLobby";
|
|
|
|
[JsonProperty("password")]
|
|
public string? Password { get; set; }
|
|
|
|
[JsonProperty("playAreaCenter")]
|
|
public Position? PlayAreaCenter { get; set; }
|
|
|
|
[JsonProperty("playAreaRadius")]
|
|
public double PlayAreaRadius { get; set; } = 500;
|
|
|
|
[JsonProperty("impostorCount")]
|
|
public int ImpostorCount { get; set; } = 1;
|
|
|
|
[JsonProperty("taskCount")]
|
|
public int TaskCount { get; set; } = 5;
|
|
}
|
|
|
|
public class CreateLobbyResponse : Message
|
|
{
|
|
public override string Type => "CreateLobbyResponse";
|
|
|
|
[JsonProperty("success")]
|
|
public bool Success { get; set; }
|
|
|
|
[JsonProperty("joinCode")]
|
|
public string? JoinCode { get; set; }
|
|
|
|
[JsonProperty("lobbyId")]
|
|
public string? LobbyId { get; set; }
|
|
|
|
[JsonProperty("error")]
|
|
public string? Error { get; set; }
|
|
|
|
[JsonProperty("lobbyState")]
|
|
public LobbyState? LobbyState { get; set; }
|
|
}
|
|
|
|
public class JoinLobby : Message
|
|
{
|
|
public override string Type => "JoinLobby";
|
|
|
|
[JsonProperty("joinCode")]
|
|
public string JoinCode { get; set; } = "";
|
|
|
|
[JsonProperty("password")]
|
|
public string? Password { get; set; }
|
|
}
|
|
|
|
public class JoinLobbyResponse : Message
|
|
{
|
|
public override string Type => "JoinLobbyResponse";
|
|
|
|
[JsonProperty("success")]
|
|
public bool Success { get; set; }
|
|
|
|
[JsonProperty("lobbyId")]
|
|
public string? LobbyId { get; set; }
|
|
|
|
[JsonProperty("error")]
|
|
public string? Error { get; set; }
|
|
|
|
[JsonProperty("lobbyState")]
|
|
public LobbyState? LobbyState { get; set; }
|
|
}
|
|
|
|
public class LeaveLobby : Message
|
|
{
|
|
public override string Type => "LeaveLobby";
|
|
}
|
|
|
|
public class ReturnToLobby : Message
|
|
{
|
|
public override string Type => "ReturnToLobby";
|
|
}
|
|
|
|
public class StartGame : Message
|
|
{
|
|
public override string Type => "StartGame";
|
|
}
|
|
|
|
// Client confirmation that map data was received
|
|
public class MapDataReceived : Message
|
|
{
|
|
public override string Type => "MapDataReceived";
|
|
}
|
|
|
|
// Hra
|
|
public class UpdatePosition : Message
|
|
{
|
|
public override string Type => "UpdatePosition";
|
|
|
|
[JsonProperty("position")]
|
|
public Position Position { get; set; }
|
|
}
|
|
|
|
public class PositionBroadcast : Message
|
|
{
|
|
public override string Type => "PositionBroadcast";
|
|
|
|
[JsonProperty("players")]
|
|
public List<PlayerPositionInfo> Players { get; set; } = new List<PlayerPositionInfo>();
|
|
}
|
|
|
|
public class PlayerPositionInfo
|
|
{
|
|
[JsonProperty("clientUuid")]
|
|
public string ClientUuid { get; set; } = "";
|
|
|
|
[JsonProperty("position")]
|
|
public Position Position { get; set; }
|
|
|
|
[JsonProperty("state")]
|
|
public PlayerState State { get; set; }
|
|
}
|
|
|
|
public class KillAttempt : Message
|
|
{
|
|
public override string Type => "KillAttempt";
|
|
|
|
[JsonProperty("targetClientUuid")]
|
|
public string TargetClientUuid { get; set; } = "";
|
|
}
|
|
|
|
public class ReportBody : Message
|
|
{
|
|
public override string Type => "ReportBody";
|
|
|
|
[JsonProperty("bodyId")]
|
|
public string BodyId { get; set; } = "";
|
|
}
|
|
|
|
public class CallEmergencyMeeting : Message
|
|
{
|
|
public override string Type => "CallEmergencyMeeting";
|
|
}
|
|
|
|
public class CastVote : Message
|
|
{
|
|
public override string Type => "CastVote";
|
|
|
|
[JsonProperty("targetClientUuid")]
|
|
public string? TargetClientUuid { get; set; }
|
|
}
|
|
|
|
public class TaskStart : Message
|
|
{
|
|
public override string Type => "TaskStart";
|
|
|
|
[JsonProperty("taskId")]
|
|
public string TaskId { get; set; } = "";
|
|
}
|
|
|
|
public class TaskProgress : Message
|
|
{
|
|
public override string Type => "TaskProgress";
|
|
|
|
[JsonProperty("taskId")]
|
|
public string TaskId { get; set; } = "";
|
|
|
|
[JsonProperty("step")]
|
|
public int Step { get; set; } = 1;
|
|
}
|
|
|
|
public class TaskComplete : Message
|
|
{
|
|
public override string Type => "TaskComplete";
|
|
|
|
[JsonProperty("taskId")]
|
|
public string TaskId { get; set; } = "";
|
|
}
|
|
|
|
public class Ping : Message
|
|
{
|
|
public override string Type => "Ping";
|
|
|
|
[JsonProperty("clientTime")]
|
|
public long ClientTime { get; set; }
|
|
}
|
|
|
|
public class Pong : Message
|
|
{
|
|
public override string Type => "Pong";
|
|
|
|
[JsonProperty("clientTime")]
|
|
public long ClientTime { get; set; }
|
|
|
|
[JsonProperty("serverTime")]
|
|
public long ServerTime { get; set; }
|
|
}
|
|
|
|
public class Reconnect : Message
|
|
{
|
|
public override string Type => "Reconnect";
|
|
|
|
[JsonProperty("lobbyId")]
|
|
public string LobbyId { get; set; } = "";
|
|
|
|
[JsonProperty("lastEventId")]
|
|
public long LastEventId { get; set; }
|
|
}
|
|
|
|
public class Ack : Message
|
|
{
|
|
public override string Type => "Ack";
|
|
|
|
[JsonProperty("ackedSeq")]
|
|
public int AckedSeq { get; set; }
|
|
|
|
[JsonProperty("success")]
|
|
public bool Success { get; set; }
|
|
|
|
[JsonProperty("error")]
|
|
public string? Error { get; set; }
|
|
}
|
|
|
|
public class ErrorMessage : Message
|
|
{
|
|
public override string Type => "Error";
|
|
|
|
[JsonProperty("errorCode")]
|
|
public string ErrorCode { get; set; } = "";
|
|
|
|
[JsonProperty("errorText")]
|
|
public string ErrorText { get; set; } = "";
|
|
}
|
|
|
|
// Sabotage messages
|
|
public class StartSabotage : Message
|
|
{
|
|
public override string Type => "StartSabotage";
|
|
|
|
[JsonProperty("sabotageType")]
|
|
public SabotageType SabotageType { get; set; }
|
|
}
|
|
|
|
public class ActivateRepairStation : Message
|
|
{
|
|
public override string Type => "ActivateRepairStation";
|
|
|
|
[JsonProperty("stationId")]
|
|
public string StationId { get; set; } = "";
|
|
}
|
|
|
|
public class DeactivateRepairStation : Message
|
|
{
|
|
public override string Type => "DeactivateRepairStation";
|
|
|
|
[JsonProperty("stationId")]
|
|
public string StationId { get; set; } = "";
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Eventy
|
|
|
|
public class GameEvent : Message
|
|
{
|
|
public override string Type => "GameEvent";
|
|
|
|
[JsonProperty("eventId")]
|
|
public long EventId { get; set; }
|
|
|
|
[JsonProperty("serverSeq")]
|
|
public long ServerSeq { get; set; }
|
|
|
|
[JsonProperty("timestamp")]
|
|
public DateTime Timestamp { get; set; }
|
|
|
|
[JsonProperty("actor")]
|
|
public string? Actor { get; set; }
|
|
|
|
[JsonProperty("eventType")]
|
|
public string EventType { get; set; } = "";
|
|
|
|
[JsonProperty("payload")]
|
|
public JObject? Payload { get; set; }
|
|
|
|
public T? GetPayload<T>() where T : class
|
|
{
|
|
if (Payload == null) return null;
|
|
return Payload.ToObject<T>(JsonSerializer.Create(JsonOptions.Default));
|
|
}
|
|
}
|
|
|
|
// Payload typy
|
|
public class HostChangedPayload
|
|
{
|
|
[JsonProperty("newHostId")]
|
|
public string NewHostId { get; set; } = "";
|
|
|
|
[JsonProperty("previousHostId")]
|
|
public string PreviousHostId { get; set; } = "";
|
|
}
|
|
|
|
public class PlayerJoinedPayload
|
|
{
|
|
[JsonProperty("clientUuid")]
|
|
public string ClientUuid { get; set; } = "";
|
|
|
|
[JsonProperty("displayName")]
|
|
public string DisplayName { get; set; } = "";
|
|
}
|
|
|
|
public class PlayerLeftPayload
|
|
{
|
|
[JsonProperty("clientUuid")]
|
|
public string ClientUuid { get; set; } = "";
|
|
|
|
[JsonProperty("reason")]
|
|
public string? Reason { get; set; }
|
|
}
|
|
|
|
// Loading phase payloads
|
|
public class GameStartingPayload
|
|
{
|
|
[JsonProperty("message")]
|
|
public string Message { get; set; } = "";
|
|
}
|
|
|
|
public class MapDataReadyPayload
|
|
{
|
|
[JsonProperty("mapData")]
|
|
public MapDataPayload? MapData { get; set; }
|
|
}
|
|
|
|
public class PlayerMapDataReceivedPayload
|
|
{
|
|
[JsonProperty("clientUuid")]
|
|
public string ClientUuid { get; set; } = "";
|
|
|
|
[JsonProperty("displayName")]
|
|
public string DisplayName { get; set; } = "";
|
|
|
|
[JsonProperty("playersReady")]
|
|
public int PlayersReady { get; set; }
|
|
|
|
[JsonProperty("totalPlayers")]
|
|
public int TotalPlayers { get; set; }
|
|
}
|
|
|
|
public class GameStartedPayload
|
|
{
|
|
[JsonProperty("impostorCount")]
|
|
public int ImpostorCount { get; set; }
|
|
|
|
[JsonProperty("taskCount")]
|
|
public int TaskCount { get; set; }
|
|
}
|
|
|
|
public class RoleAssignedPayload
|
|
{
|
|
[JsonProperty("clientUuid")]
|
|
public string ClientUuid { get; set; } = "";
|
|
|
|
[JsonProperty("role")]
|
|
public PlayerRole Role { get; set; }
|
|
|
|
[JsonProperty("tasks")]
|
|
public List<GameTask>? Tasks { get; set; }
|
|
}
|
|
|
|
public class PlayerKilledPayload
|
|
{
|
|
[JsonProperty("victimId")]
|
|
public string VictimId { get; set; } = "";
|
|
|
|
[JsonProperty("killerId")]
|
|
public string KillerId { get; set; } = "";
|
|
|
|
[JsonProperty("bodyId")]
|
|
public string BodyId { get; set; } = "";
|
|
|
|
[JsonProperty("location")]
|
|
public Position Location { get; set; }
|
|
}
|
|
|
|
public class BodyReportedPayload
|
|
{
|
|
[JsonProperty("reporterId")]
|
|
public string ReporterId { get; set; } = "";
|
|
|
|
[JsonProperty("bodyId")]
|
|
public string BodyId { get; set; } = "";
|
|
|
|
[JsonProperty("victimId")]
|
|
public string VictimId { get; set; } = "";
|
|
}
|
|
|
|
public class EmergencyMeetingCalledPayload
|
|
{
|
|
[JsonProperty("callerId")]
|
|
public string CallerId { get; set; } = "";
|
|
}
|
|
|
|
public class MeetingStartedPayload
|
|
{
|
|
[JsonProperty("meetingId")]
|
|
public string MeetingId { get; set; } = "";
|
|
|
|
[JsonProperty("type")]
|
|
public MeetingType Type { get; set; }
|
|
|
|
[JsonProperty("meetingLocation")]
|
|
public Position MeetingLocation { get; set; }
|
|
|
|
[JsonProperty("arrivalDeadline")]
|
|
public DateTime ArrivalDeadline { get; set; }
|
|
|
|
[JsonProperty("discussionEndTime")]
|
|
public DateTime? DiscussionEndTime { get; set; }
|
|
|
|
[JsonProperty("votingEndTime")]
|
|
public DateTime VotingEndTime { get; set; }
|
|
}
|
|
|
|
public class PlayerArrivedAtMeetingPayload
|
|
{
|
|
[JsonProperty("clientUuid")]
|
|
public string ClientUuid { get; set; } = "";
|
|
|
|
[JsonProperty("meetingId")]
|
|
public string MeetingId { get; set; } = "";
|
|
}
|
|
|
|
public class PlayerVotedPayload
|
|
{
|
|
[JsonProperty("voterId")]
|
|
public string VoterId { get; set; } = "";
|
|
|
|
[JsonProperty("targetId")]
|
|
public string? TargetId { get; set; }
|
|
}
|
|
|
|
public class VotingClosedPayload
|
|
{
|
|
[JsonProperty("voteCounts")]
|
|
public Dictionary<string, int> VoteCounts { get; set; } = new Dictionary<string, int>();
|
|
|
|
[JsonProperty("ejectedPlayerId")]
|
|
public string? EjectedPlayerId { get; set; }
|
|
|
|
[JsonProperty("wasTie")]
|
|
public bool WasTie { get; set; }
|
|
}
|
|
|
|
public class PlayerEjectedPayload
|
|
{
|
|
[JsonProperty("clientUuid")]
|
|
public string ClientUuid { get; set; } = "";
|
|
|
|
[JsonProperty("role")]
|
|
public PlayerRole Role { get; set; }
|
|
}
|
|
|
|
public class TaskCompletedPayload
|
|
{
|
|
[JsonProperty("clientUuid")]
|
|
public string ClientUuid { get; set; } = "";
|
|
|
|
[JsonProperty("taskId")]
|
|
public string TaskId { get; set; } = "";
|
|
|
|
[JsonProperty("totalCompleted")]
|
|
public int TotalCompleted { get; set; }
|
|
|
|
[JsonProperty("totalTasks")]
|
|
public int TotalTasks { get; set; }
|
|
}
|
|
|
|
public class GameEndedPayload
|
|
{
|
|
[JsonProperty("winningFaction")]
|
|
public string WinningFaction { get; set; } = "";
|
|
|
|
[JsonProperty("reason")]
|
|
public string Reason { get; set; } = "";
|
|
|
|
[JsonProperty("winners")]
|
|
public List<string> Winners { get; set; } = new List<string>();
|
|
}
|
|
|
|
public class ReturnedToLobbyPayload
|
|
{
|
|
[JsonProperty("message")]
|
|
public string Message { get; set; } = "";
|
|
}
|
|
|
|
// System message payload (admin broadcast)
|
|
public class SystemMessagePayload
|
|
{
|
|
[JsonProperty("message")]
|
|
public string Message { get; set; } = "";
|
|
|
|
[JsonProperty("timestamp")]
|
|
public DateTime Timestamp { get; set; }
|
|
}
|
|
|
|
// Sabotage event payloads
|
|
public class SabotageStartedPayload
|
|
{
|
|
[JsonProperty("sabotageId")]
|
|
public string SabotageId { get; set; } = "";
|
|
|
|
[JsonProperty("type")]
|
|
public SabotageType Type { get; set; }
|
|
|
|
[JsonProperty("initiatorId")]
|
|
public string InitiatorId { get; set; } = "";
|
|
|
|
[JsonProperty("deadline")]
|
|
public DateTime? Deadline { get; set; }
|
|
|
|
[JsonProperty("repairStations")]
|
|
public List<RepairStationInfo> RepairStations { get; set; } = new List<RepairStationInfo>();
|
|
|
|
[JsonProperty("requiredSimultaneousRepairs")]
|
|
public int RequiredSimultaneousRepairs { get; set; }
|
|
}
|
|
|
|
public class RepairStationInfo
|
|
{
|
|
[JsonProperty("stationId")]
|
|
public string StationId { get; set; } = "";
|
|
|
|
[JsonProperty("name")]
|
|
public string Name { get; set; } = "";
|
|
|
|
[JsonProperty("location")]
|
|
public Position Location { get; set; }
|
|
|
|
[JsonProperty("repairDurationMs")]
|
|
public int RepairDurationMs { get; set; }
|
|
|
|
/// <summary>
|
|
/// Track locally if this station has been repaired
|
|
/// </summary>
|
|
[JsonIgnore]
|
|
public bool IsRepaired { get; set; }
|
|
}
|
|
|
|
public class RepairStartedPayload
|
|
{
|
|
[JsonProperty("sabotageId")]
|
|
public string SabotageId { get; set; } = "";
|
|
|
|
[JsonProperty("stationId")]
|
|
public string StationId { get; set; } = "";
|
|
|
|
[JsonProperty("playerId")]
|
|
public string PlayerId { get; set; } = "";
|
|
}
|
|
|
|
public class RepairStoppedPayload
|
|
{
|
|
[JsonProperty("sabotageId")]
|
|
public string SabotageId { get; set; } = "";
|
|
|
|
[JsonProperty("stationId")]
|
|
public string StationId { get; set; } = "";
|
|
|
|
[JsonProperty("playerId")]
|
|
public string PlayerId { get; set; } = "";
|
|
}
|
|
|
|
public class SabotageRepairedPayload
|
|
{
|
|
[JsonProperty("sabotageId")]
|
|
public string SabotageId { get; set; } = "";
|
|
|
|
[JsonProperty("type")]
|
|
public SabotageType Type { get; set; }
|
|
|
|
[JsonProperty("repairerIds")]
|
|
public List<string> RepairerIds { get; set; } = new List<string>();
|
|
}
|
|
|
|
public class SabotageMeltdownPayload
|
|
{
|
|
[JsonProperty("sabotageId")]
|
|
public string SabotageId { get; set; } = "";
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region State
|
|
|
|
public class LobbyState
|
|
{
|
|
[JsonProperty("lobbyId")]
|
|
public string LobbyId { get; set; } = "";
|
|
|
|
[JsonProperty("joinCode")]
|
|
public string JoinCode { get; set; } = "";
|
|
|
|
[JsonProperty("ownerId")]
|
|
public string? OwnerId { get; set; }
|
|
|
|
[JsonProperty("phase")]
|
|
public GamePhase Phase { get; set; }
|
|
|
|
[JsonProperty("players")]
|
|
public List<PlayerInfo> Players { get; set; } = new List<PlayerInfo>();
|
|
|
|
[JsonProperty("playAreaCenter")]
|
|
public Position PlayAreaCenter { get; set; }
|
|
|
|
[JsonProperty("playAreaRadius")]
|
|
public double PlayAreaRadius { get; set; }
|
|
|
|
[JsonProperty("impostorCount")]
|
|
public int ImpostorCount { get; set; }
|
|
|
|
[JsonProperty("hasPassword")]
|
|
public bool HasPassword { get; set; }
|
|
|
|
[JsonProperty("mapData")]
|
|
public MapDataPayload? MapData { get; set; }
|
|
|
|
/// <summary>True if map data has been loaded (or Overpass is disabled)</summary>
|
|
[JsonProperty("mapDataReady")]
|
|
public bool MapDataReady { get; set; } = true;
|
|
}
|
|
|
|
// Map data classes for rendering - compact format from server
|
|
public class MapDataPayload
|
|
{
|
|
[JsonProperty("center")]
|
|
public Position Center { get; set; }
|
|
|
|
[JsonProperty("radiusMeters")]
|
|
public double RadiusMeters { get; set; }
|
|
|
|
/// <summary>Buildings: [[lat,lon,lat,lon,...], ...]</summary>
|
|
[JsonProperty("buildings")]
|
|
public List<double[]> Buildings { get; set; } = new List<double[]>();
|
|
|
|
/// <summary>Building types: ["residential", "commercial", ...]</summary>
|
|
[JsonProperty("buildingTypes")]
|
|
public List<string> BuildingTypes { get; set; } = new List<string>();
|
|
|
|
/// <summary>Pathways: [[lat,lon,lat,lon,...], ...]</summary>
|
|
[JsonProperty("pathways")]
|
|
public List<double[]> Pathways { get; set; } = new List<double[]>();
|
|
|
|
/// <summary>Pathway types: [0=footway, 1=steps, ...]</summary>
|
|
[JsonProperty("pathwayTypes")]
|
|
public List<int> PathwayTypes { get; set; } = new List<int>();
|
|
|
|
/// <summary>Areas (parks): [[lat,lon,lat,lon,...], ...]</summary>
|
|
[JsonProperty("areas")]
|
|
public List<double[]> Areas { get; set; } = new List<double[]>();
|
|
|
|
/// <summary>Area types</summary>
|
|
[JsonProperty("areaTypes")]
|
|
public List<int> AreaTypes { get; set; } = new List<int>();
|
|
|
|
/// <summary>POIs: [lat, lon, type, lat, lon, type, ...]</summary>
|
|
[JsonProperty("pOIs")]
|
|
public List<double> POIs { get; set; } = new List<double>();
|
|
|
|
// Helper methods for extracting structured data
|
|
public List<MapBuilding> GetBuildings()
|
|
{
|
|
var result = new List<MapBuilding>();
|
|
for (int i = 0; i < Buildings.Count; i++)
|
|
{
|
|
var coords = Buildings[i];
|
|
var outline = new List<Position>();
|
|
for (int j = 0; j < coords.Length - 1; j += 2)
|
|
{
|
|
outline.Add(new Position(coords[j], coords[j + 1]));
|
|
}
|
|
result.Add(new MapBuilding
|
|
{
|
|
Id = i,
|
|
Outline = outline,
|
|
BuildingType = i < BuildingTypes.Count ? BuildingTypes[i] : "yes"
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public List<MapPathway> GetPathways()
|
|
{
|
|
var result = new List<MapPathway>();
|
|
for (int i = 0; i < Pathways.Count; i++)
|
|
{
|
|
var coords = Pathways[i];
|
|
var points = new List<Position>();
|
|
for (int j = 0; j < coords.Length - 1; j += 2)
|
|
{
|
|
points.Add(new Position(coords[j], coords[j + 1]));
|
|
}
|
|
result.Add(new MapPathway
|
|
{
|
|
Id = i,
|
|
Points = points,
|
|
PathType = i < PathwayTypes.Count ? (PathType)PathwayTypes[i] : PathType.Other
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public List<MapArea> GetAreas()
|
|
{
|
|
var result = new List<MapArea>();
|
|
for (int i = 0; i < Areas.Count; i++)
|
|
{
|
|
var coords = Areas[i];
|
|
var outline = new List<Position>();
|
|
for (int j = 0; j < coords.Length - 1; j += 2)
|
|
{
|
|
outline.Add(new Position(coords[j], coords[j + 1]));
|
|
}
|
|
result.Add(new MapArea
|
|
{
|
|
Id = i,
|
|
Outline = outline,
|
|
AreaType = i < AreaTypes.Count ? (MapAreaType)AreaTypes[i] : MapAreaType.Other
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public List<MapPOI> GetPOIs()
|
|
{
|
|
var result = new List<MapPOI>();
|
|
for (int i = 0; i < POIs.Count - 2; i += 3)
|
|
{
|
|
result.Add(new MapPOI
|
|
{
|
|
Id = i / 3,
|
|
Location = new Position(POIs[i], POIs[i + 1]),
|
|
POIType = (MapPOIType)(int)POIs[i + 2]
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public class MapBuilding
|
|
{
|
|
public long Id { get; set; }
|
|
public List<Position> Outline { get; set; } = new List<Position>();
|
|
public string? Name { get; set; }
|
|
public string? BuildingType { get; set; }
|
|
}
|
|
|
|
public class MapPathway
|
|
{
|
|
public long Id { get; set; }
|
|
public List<Position> Points { get; set; } = new List<Position>();
|
|
public PathType PathType { get; set; }
|
|
public string? Name { get; set; }
|
|
}
|
|
|
|
public class MapArea
|
|
{
|
|
public long Id { get; set; }
|
|
public List<Position> Outline { get; set; } = new List<Position>();
|
|
public MapAreaType AreaType { get; set; }
|
|
public string? Name { get; set; }
|
|
}
|
|
|
|
public class MapPOI
|
|
{
|
|
public long Id { get; set; }
|
|
public Position Location { get; set; }
|
|
public string? Name { get; set; }
|
|
public MapPOIType POIType { get; set; }
|
|
}
|
|
|
|
public class PlayerInfo
|
|
{
|
|
[JsonProperty("clientUuid")]
|
|
public string ClientUuid { get; set; } = "";
|
|
|
|
[JsonProperty("displayName")]
|
|
public string DisplayName { get; set; } = "";
|
|
|
|
[JsonProperty("isOwner")]
|
|
public bool IsOwner { get; set; }
|
|
|
|
[JsonProperty("isReady")]
|
|
public bool IsReady { get; set; }
|
|
|
|
[JsonProperty("state")]
|
|
public PlayerState State { get; set; }
|
|
}
|
|
|
|
public class GameTask
|
|
{
|
|
[JsonProperty("taskId")]
|
|
public string TaskId { get; set; } = "";
|
|
|
|
[JsonProperty("name")]
|
|
public string Name { get; set; } = "";
|
|
|
|
[JsonProperty("location")]
|
|
public Position Location { get; set; }
|
|
|
|
[JsonProperty("type")]
|
|
public TaskType Type { get; set; } = TaskType.Instant;
|
|
}
|
|
|
|
public class Body
|
|
{
|
|
[JsonProperty("bodyId")]
|
|
public string BodyId { get; set; } = "";
|
|
|
|
[JsonProperty("victimId")]
|
|
public string VictimId { get; set; } = "";
|
|
|
|
[JsonProperty("location")]
|
|
public Position Location { get; set; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Serializace
|
|
|
|
public static class JsonOptions
|
|
{
|
|
public static readonly JsonSerializerSettings Default = new JsonSerializerSettings
|
|
{
|
|
NullValueHandling = NullValueHandling.Ignore,
|
|
Converters = { new StringEnumConverter() }
|
|
};
|
|
}
|
|
|
|
public static class MessageSerializer
|
|
{
|
|
private static readonly Dictionary<string, Type> MessageTypes = new Dictionary<string, Type>
|
|
{
|
|
["ClientHello"] = typeof(ClientHello),
|
|
["ServerHello"] = typeof(ServerHello),
|
|
["KeyExchange"] = typeof(KeyExchange),
|
|
["KeyExchangeAck"] = typeof(KeyExchangeAck),
|
|
["CreateLobby"] = typeof(CreateLobby),
|
|
["CreateLobbyResponse"] = typeof(CreateLobbyResponse),
|
|
["JoinLobby"] = typeof(JoinLobby),
|
|
["JoinLobbyResponse"] = typeof(JoinLobbyResponse),
|
|
["LeaveLobby"] = typeof(LeaveLobby),
|
|
["ReturnToLobby"] = typeof(ReturnToLobby),
|
|
["StartGame"] = typeof(StartGame),
|
|
["MapDataReceived"] = typeof(MapDataReceived),
|
|
["UpdatePosition"] = typeof(UpdatePosition),
|
|
["PositionBroadcast"] = typeof(PositionBroadcast),
|
|
["KillAttempt"] = typeof(KillAttempt),
|
|
["ReportBody"] = typeof(ReportBody),
|
|
["CallEmergencyMeeting"] = typeof(CallEmergencyMeeting),
|
|
["CastVote"] = typeof(CastVote),
|
|
["TaskStart"] = typeof(TaskStart),
|
|
["TaskProgress"] = typeof(TaskProgress),
|
|
["TaskComplete"] = typeof(TaskComplete),
|
|
["Ping"] = typeof(Ping),
|
|
["Pong"] = typeof(Pong),
|
|
["Reconnect"] = typeof(Reconnect),
|
|
["Ack"] = typeof(Ack),
|
|
["Error"] = typeof(ErrorMessage),
|
|
["GameEvent"] = typeof(GameEvent)
|
|
};
|
|
|
|
public static byte[] Serialize(Message msg)
|
|
{
|
|
var json = JsonConvert.SerializeObject(msg, JsonOptions.Default);
|
|
return Encoding.UTF8.GetBytes(json);
|
|
}
|
|
|
|
public static Message? Deserialize(byte[] data)
|
|
{
|
|
var json = Encoding.UTF8.GetString(data);
|
|
var jObj = JObject.Parse(json);
|
|
|
|
var typeName = jObj["type"]?.Value<string>();
|
|
if (typeName == null || !MessageTypes.TryGetValue(typeName, out var type))
|
|
return null;
|
|
|
|
return (Message?)JsonConvert.DeserializeObject(json, type, JsonOptions.Default);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|