mirror of
https://github.com/SoDOff-Project/sodoff-mmo.git
synced 2025-10-11 08:18:49 -07:00
support for Thunder Run Racing
This commit is contained in:
parent
bfbd9adb82
commit
aa373cd2a9
31
src/CommandHandlers/PublicMessageHandlers.cs
Normal file
31
src/CommandHandlers/PublicMessageHandlers.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using sodoffmmo.Attributes;
|
||||||
|
using sodoffmmo.Core;
|
||||||
|
using sodoffmmo.Data;
|
||||||
|
|
||||||
|
namespace sodoffmmo.CommandHandlers;
|
||||||
|
|
||||||
|
[ExtensionCommandHandler("PM")]
|
||||||
|
class RacingPMHandler : ICommandHandler
|
||||||
|
{
|
||||||
|
// rec: {"a":13,"c":1,"p":{"c":"PM","p":{"M":"DT:c4647597-a72a-4f34-973c-5a10218d9a64:1000","en":"we"},"r":-1}}
|
||||||
|
public void Handle(Client client, NetworkObject receivedObject) {
|
||||||
|
// send: {"a":13,"c":1,"p":{"c":"PM","p":{"arr":[{"M":["DT:f05fc387-7358-4bff-be04-7c316f0a8de8:1000"],"MID":3529441}]}}}
|
||||||
|
NetworkObject cmd = new();
|
||||||
|
NetworkObject p = new();
|
||||||
|
NetworkArray arr = new();
|
||||||
|
NetworkObject data = new();
|
||||||
|
data.Add("M", new string[] {
|
||||||
|
receivedObject.Get<NetworkObject>("p").Get<string>("M")
|
||||||
|
});
|
||||||
|
arr.Add(data);
|
||||||
|
p.Add("arr", arr);
|
||||||
|
p.Add("MID", client.ClientID);
|
||||||
|
cmd.Add("c", "PM");
|
||||||
|
cmd.Add("p", p);
|
||||||
|
NetworkPacket packet = NetworkObject.WrapObject(1, 13, cmd).Serialize();
|
||||||
|
|
||||||
|
foreach (var roomClient in client.Room.Clients) {
|
||||||
|
roomClient.Send(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
80
src/CommandHandlers/RacingHandlers.cs
Normal file
80
src/CommandHandlers/RacingHandlers.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
using sodoffmmo.Attributes;
|
||||||
|
using sodoffmmo.Core;
|
||||||
|
using sodoffmmo.Data;
|
||||||
|
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
namespace sodoffmmo.CommandHandlers;
|
||||||
|
|
||||||
|
// Set Player Ready
|
||||||
|
[ExtensionCommandHandler("dr.PR")]
|
||||||
|
class RacingPlayerReadyHandler : ICommandHandler
|
||||||
|
{
|
||||||
|
public void Handle(Client client, NetworkObject receivedObject) { // {"a":13,"c":1,"p":{"c":"dr.PR","p":{"IMR":"True","en":""},"r":-1}}
|
||||||
|
NetworkObject p = receivedObject.Get<NetworkObject>("p");
|
||||||
|
RacingPlayerState ready = p.Get<string>("IMR") == "True" ? RacingPlayerState.Ready : RacingPlayerState.NotReady;
|
||||||
|
|
||||||
|
// client send also {"a":7,"c":0,"p":{"m":"IMR:ae70ef16-c52c-43e4-9305-d6ea3e378a0d:True","r":412456,"t":0,"u":3529456}}
|
||||||
|
// so server do not need generate this packet
|
||||||
|
|
||||||
|
if (client.Room.Group == "RacingDragon") {
|
||||||
|
RacingRoom room = client.Room as RacingRoom;
|
||||||
|
room.SetPlayerState(client, ready);
|
||||||
|
Console.WriteLine($"IMR Lobby: {client.ClientID} {ready}");
|
||||||
|
room.TryLoad();
|
||||||
|
} else {
|
||||||
|
RacingLobby.SetPlayerState(client, ready);
|
||||||
|
Console.WriteLine($"IMR: {client.ClientID} {ready}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player Status Request
|
||||||
|
[ExtensionCommandHandler("dr.PS")]
|
||||||
|
class RacingPlayerStatusHandler : ICommandHandler
|
||||||
|
{
|
||||||
|
public void Handle(Client client, NetworkObject receivedObject) {
|
||||||
|
RacingLobby.SendToRacingRoom();
|
||||||
|
client.Send(RacingLobby.GetPS());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// User Ready ACK
|
||||||
|
[ExtensionCommandHandler("dr.UACK")]
|
||||||
|
class RacingUACKHandler : ICommandHandler
|
||||||
|
{
|
||||||
|
public void Handle(Client client, NetworkObject receivedObject) {
|
||||||
|
RacingRoom room = client.Room as RacingRoom;
|
||||||
|
room.SetPlayerState(client, RacingPlayerState.RaceReady1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All Ready ACK
|
||||||
|
[ExtensionCommandHandler("dr.ARACK")]
|
||||||
|
class RacingARACKHandler : ICommandHandler
|
||||||
|
{
|
||||||
|
public void Handle(Client client, NetworkObject receivedObject) {
|
||||||
|
RacingRoom room = client.Room as RacingRoom;
|
||||||
|
room.SetPlayerState(client, RacingPlayerState.RaceReady2);
|
||||||
|
|
||||||
|
if (room.GetPlayersCount(RacingPlayerState.RaceReady2) == room.ClientsCount) {
|
||||||
|
NetworkPacket packet = room.GetSTAPacket();
|
||||||
|
foreach (var roomClient in room.Clients) {
|
||||||
|
roomClient.Send(packet);
|
||||||
|
}
|
||||||
|
Console.WriteLine($"STA");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExtensionCommandHandler("dr.AR")]
|
||||||
|
class RacingARHandler : ICommandHandler
|
||||||
|
{
|
||||||
|
public void Handle(Client client, NetworkObject receivedObject) { // {"a":13,"c":1,"p":{"c":"dr.AR","p":{"CT":"112.1268","FD":"3008.283","LC":"3","UN":"scourgexxwulf","en":""},"r":412467}}
|
||||||
|
RacingRoom room = client.Room as RacingRoom;
|
||||||
|
NetworkObject p = receivedObject.Get<NetworkObject>("p");
|
||||||
|
|
||||||
|
room.SetResults(client, p.Get<string>("UN"), p.Get<string>("CT"), p.Get<string>("LC"));
|
||||||
|
room.SendResults();
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,8 @@ internal sealed class ServerConfiguration {
|
|||||||
public int Port { get; set; } = 9933;
|
public int Port { get; set; } = 9933;
|
||||||
public int EventTimer { get; set; } = 30;
|
public int EventTimer { get; set; } = 30;
|
||||||
public int FirstEventTimer { get; set; } = 10;
|
public int FirstEventTimer { get; set; } = 10;
|
||||||
|
public int RacingMaxPlayers { get; set; } = 6;
|
||||||
|
public int RacingMinPlayers { get; set; } = 2;
|
||||||
public bool EnableChat { get; set; } = true;
|
public bool EnableChat { get; set; } = true;
|
||||||
public bool AllowChaos { get; set; } = false;
|
public bool AllowChaos { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
312
src/Core/Racing.cs
Normal file
312
src/Core/Racing.cs
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using sodoffmmo.Data;
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
namespace sodoffmmo.Core;
|
||||||
|
|
||||||
|
public enum RacingPlayerState {
|
||||||
|
NotReady,
|
||||||
|
Ready,
|
||||||
|
InRacingRoom,
|
||||||
|
RaceReady1,
|
||||||
|
RaceReady2
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RacingRoom : Room {
|
||||||
|
static List<RacingRoom> RacingRooms = new();
|
||||||
|
static Random random = new Random();
|
||||||
|
|
||||||
|
public static RacingRoom Get() {
|
||||||
|
foreach (var room in RacingRooms) {
|
||||||
|
if (room.ClientsCount == 0) {
|
||||||
|
room.Reset();
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newroom = new RacingRoom("RacingDragon" + "_" + RacingRooms.Count.ToString());
|
||||||
|
RacingRooms.Add(newroom);
|
||||||
|
return newroom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RacingRoom(string name, int? trackId = null) : base (name, "RacingDragon") {
|
||||||
|
Reset(trackId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reset(int? trackId = null) {
|
||||||
|
if (trackId is null) {
|
||||||
|
TID = random.Next(105);
|
||||||
|
} else {
|
||||||
|
TID = (int)trackId;
|
||||||
|
}
|
||||||
|
|
||||||
|
players = new();
|
||||||
|
results = new();
|
||||||
|
timer = null;
|
||||||
|
|
||||||
|
base.RoomVariables = new();
|
||||||
|
base.RoomVariables.Add(NetworkArray.VlElement("IS_RACE_ROOM", "SINGLERACE#1#1#SINGLERACE#0#2"));
|
||||||
|
base.RoomVariables.Add(NetworkArray.VlElement("TID", TID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TID;
|
||||||
|
|
||||||
|
// players ready status
|
||||||
|
|
||||||
|
Dictionary<Client, RacingPlayerState> players;
|
||||||
|
|
||||||
|
public void SetPlayerState(Client client, RacingPlayerState state) {
|
||||||
|
players[client] = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPlayerState(Client client, RacingPlayerState state) {
|
||||||
|
if (players.TryGetValue(client, out var info)) {
|
||||||
|
return info == state;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetPlayersCount(RacingPlayerState state) {
|
||||||
|
int count = 0;
|
||||||
|
foreach(var player in players) {
|
||||||
|
if (player.Value == state) ++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// results
|
||||||
|
|
||||||
|
class Result {
|
||||||
|
public string userName;
|
||||||
|
public string time;
|
||||||
|
public string laps;
|
||||||
|
}
|
||||||
|
|
||||||
|
SortedDictionary<float, Result> results;
|
||||||
|
|
||||||
|
public void SetResults(Client client, string userName, string time, string laps) {
|
||||||
|
float timef = float.Parse(time, System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
while (results.ContainsKey(timef))
|
||||||
|
timef += 0.000001f;
|
||||||
|
|
||||||
|
results.Add(timef, new Result {
|
||||||
|
userName = userName,
|
||||||
|
time = time,
|
||||||
|
laps = laps
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendResults() {
|
||||||
|
if (ClientsCount == results.Count) {
|
||||||
|
// {"a":13,"c":1,"p":{"c":"","p":{"arr":["RA","","GR","Zavertin","91.81613","3","scourgexxwulf","111.81613","3"]},"r":412467}}
|
||||||
|
List<string> info = new();
|
||||||
|
info.Add("RA");
|
||||||
|
info.Add("");
|
||||||
|
info.Add("GR");
|
||||||
|
foreach(var result in results) {
|
||||||
|
info.Add(result.Value.userName);
|
||||||
|
info.Add(result.Value.time);
|
||||||
|
info.Add(result.Value.laps);
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkPacket packet = Utils.ArrNetworkPacket(info.ToArray(), "", Id);
|
||||||
|
foreach (var roomClient in Clients) {
|
||||||
|
roomClient.Send(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start and countdown
|
||||||
|
|
||||||
|
System.Timers.Timer timer;
|
||||||
|
int counter;
|
||||||
|
|
||||||
|
private void SetTimer(double timeout, System.Timers.ElapsedEventHandler callback, bool AutoReset = false) {
|
||||||
|
if (timer != null) {
|
||||||
|
timer.Stop();
|
||||||
|
timer.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = new System.Timers.Timer(timeout * 1000);
|
||||||
|
timer.AutoReset = AutoReset;
|
||||||
|
timer.Enabled = true;
|
||||||
|
timer.Elapsed += callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init() {
|
||||||
|
counter = 20;
|
||||||
|
SetTimer(0.2, SendJoin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendJoin(Object source, ElapsedEventArgs e) {
|
||||||
|
foreach(var player in players) {
|
||||||
|
Console.WriteLine($"Join Racing Room: {Name} RoomID: {Id} IID: {player.Key.ClientID}");
|
||||||
|
player.Key.JoinRoom(this);
|
||||||
|
}
|
||||||
|
SetTimer(1, CountDown, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CountDown(Object source, ElapsedEventArgs e) {
|
||||||
|
if (!TryLoad()) {
|
||||||
|
if (counter == 0) {
|
||||||
|
Load();
|
||||||
|
} else {
|
||||||
|
// {"a":13,"c":1,"p":{"c":"","p":{"arr":["RA","","LT","18"]},"r":412467}}
|
||||||
|
NetworkPacket packet = Utils.ArrNetworkPacket(new string[] {
|
||||||
|
"RA",
|
||||||
|
"",
|
||||||
|
"LT",
|
||||||
|
(--counter).ToString()
|
||||||
|
}, "", Id);
|
||||||
|
foreach (var roomClient in Clients) {
|
||||||
|
roomClient.Send(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryLoad() {
|
||||||
|
if (GetPlayersCount(RacingPlayerState.Ready) == ClientsCount) {
|
||||||
|
Load();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Load() {
|
||||||
|
timer.Stop();
|
||||||
|
timer.Close();
|
||||||
|
|
||||||
|
// {"a":13,"c":1,"p":{"c":"","p":{"arr":["RA","","ST"]},"r":412467}}
|
||||||
|
NetworkPacket packet = Utils.ArrNetworkPacket(new string[] {
|
||||||
|
"RA",
|
||||||
|
"",
|
||||||
|
"ST"
|
||||||
|
}, "", Id);
|
||||||
|
foreach (var roomClient in Clients) {
|
||||||
|
roomClient.Send(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO StratTimer → kick out to main lobby players without RacingPlayerState.RaceReady1 after timeout, next kick out players without RacingPlayerState.RaceReady2 after timeout2
|
||||||
|
|
||||||
|
// TODO EndTimer → {"a":13,"c":1,"p":{"c":"","p":{"arr":["RA","","ET","1"]},"r":412467}}
|
||||||
|
|
||||||
|
// utils
|
||||||
|
|
||||||
|
public NetworkPacket GetTIDPacket() {
|
||||||
|
// {"a":13,"c":1,"p":{"c":"","p":{"arr":["RA","","TID","","10","0","0","0","0"]}}}
|
||||||
|
return Utils.ArrNetworkPacket(new string[] {
|
||||||
|
"RA",
|
||||||
|
"",
|
||||||
|
"TID",
|
||||||
|
"", // game mode (unused)
|
||||||
|
TID.ToString(), // track id
|
||||||
|
"0", // theme id
|
||||||
|
"0","0","0" // unused (?)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetworkPacket GetSTAPacket() {
|
||||||
|
string staList = "";
|
||||||
|
int i = 0;
|
||||||
|
foreach(var player in players) {
|
||||||
|
if (i > 0)
|
||||||
|
staList += ":";
|
||||||
|
staList += player.Key.PlayerData.Uid + ":" + (++i).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// {"a":13,"c":1,"p":{"c":"","p":{"arr":["RA","","STA","c4647597-a72a-4f34-973c-5a10218d9a64:1:f05fc387-7358-4bff-be04-7c316f0a8de8:2:ae70ef16-c52c-43e4-9305-d6ea3e378a0d:3","1688128174649"]},"r":412467}}
|
||||||
|
return Utils.ArrNetworkPacket(new string[] {
|
||||||
|
"RA",
|
||||||
|
"",
|
||||||
|
"STA",
|
||||||
|
staList
|
||||||
|
}, "", Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RacingLobby {
|
||||||
|
class Status {
|
||||||
|
public string uid;
|
||||||
|
public RacingPlayerState state = RacingPlayerState.NotReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
static object lobbyLock = new object();
|
||||||
|
|
||||||
|
static Dictionary<Client, Status> lobbyPlayers = new();
|
||||||
|
|
||||||
|
public static void SetPlayerState(Client client, RacingPlayerState state) {
|
||||||
|
lock (lobbyLock) {
|
||||||
|
if (!lobbyPlayers.ContainsKey(client)) {
|
||||||
|
lobbyPlayers[client] = new Status { uid = client.PlayerData.Uid };
|
||||||
|
}
|
||||||
|
lobbyPlayers[client].state = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsPlayerState(Client client, RacingPlayerState state) {
|
||||||
|
lock (lobbyLock) {
|
||||||
|
if (lobbyPlayers.TryGetValue(client, out var info)) {
|
||||||
|
return info.state == state;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetPlayersCount(RacingPlayerState state) {
|
||||||
|
lock (lobbyLock) {
|
||||||
|
int count = 0;
|
||||||
|
foreach(var player in lobbyPlayers) {
|
||||||
|
if (player.Value.state == state) ++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SendToRacingRoom() {
|
||||||
|
lock (lobbyLock) {
|
||||||
|
if (GetPlayersCount(RacingPlayerState.Ready) >= Configuration.ServerConfiguration.RacingMinPlayers) {
|
||||||
|
int i = 0;
|
||||||
|
List<Client> toRemove = new();
|
||||||
|
RacingRoom room = RacingRoom.Get();
|
||||||
|
foreach (var player in lobbyPlayers) {
|
||||||
|
if (player.Value.state == RacingPlayerState.Ready) {
|
||||||
|
if (++i > Configuration.ServerConfiguration.RacingMaxPlayers)
|
||||||
|
break;
|
||||||
|
// set client state in Lobby
|
||||||
|
player.Value.state = RacingPlayerState.InRacingRoom;
|
||||||
|
// mark client to remove from Lobby
|
||||||
|
toRemove.Add(player.Key);
|
||||||
|
// send TID info to client
|
||||||
|
player.Key.Send(room.GetTIDPacket());
|
||||||
|
// set client state in racing room
|
||||||
|
room.SetPlayerState(player.Key, RacingPlayerState.NotReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// join clients to racing room and start countdown
|
||||||
|
room.Init();
|
||||||
|
|
||||||
|
foreach (var player in toRemove) {
|
||||||
|
lobbyPlayers.Remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NetworkPacket GetPS() {
|
||||||
|
// {"a":13,"c":1,"p":{"c":"PS","p":{"arr":["RA","","PS","e6147216-8100-4552-864d-be8f1347e201","8cb5842d-735a-4259-80af-e2e204b9c2bd"]}}}
|
||||||
|
List<string> info = new();
|
||||||
|
info.Add("RA");
|
||||||
|
info.Add("");
|
||||||
|
info.Add("PS");
|
||||||
|
foreach(var player in lobbyPlayers) {
|
||||||
|
if (player.Value.state != RacingPlayerState.InRacingRoom) {
|
||||||
|
info.Add(player.Value.uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Utils.ArrNetworkPacket(info.ToArray(), "PS");
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,12 @@
|
|||||||
"// EventTimer": "time between start of world events (battle ship events)",
|
"// EventTimer": "time between start of world events (battle ship events)",
|
||||||
"EventTimer": 30,
|
"EventTimer": 30,
|
||||||
|
|
||||||
|
"// RacingMaxPlayers": "maximum players allowed in Thunder Run Racing (no more than 6)",
|
||||||
|
"RacingMaxPlayers": 6,
|
||||||
|
|
||||||
|
"// RacingMinPlayers": "minimum players to start Thunder Run Racing",
|
||||||
|
"RacingMinPlayers": 2,
|
||||||
|
|
||||||
"// AllowChaos": "disable server side exploit protection",
|
"// AllowChaos": "disable server side exploit protection",
|
||||||
"AllowChaos": false
|
"AllowChaos": false
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user