231 lines
8.7 KiB
C#
231 lines
8.7 KiB
C#
#nullable enable
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Barotrauma.Extensions;
|
|
using Barotrauma.Networking;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
internal partial class MedicalClinic
|
|
{
|
|
// allow 20 requests per 5 seconds, announce to chat if the limit is reached
|
|
private readonly RateLimiter rateLimiter = new(
|
|
maxRequests: RateLimitMaxRequests,
|
|
expiryInSeconds: RateLimitExpiry,
|
|
punishmentRules: (RateLimitAction.OnLimitReached, RateLimitPunishment.Announce));
|
|
|
|
private readonly record struct AfflictionSubscriber(Client Subscriber, CharacterInfo Target, DateTimeOffset Expiry);
|
|
|
|
private readonly List<AfflictionSubscriber> afflictionSubscribers = new();
|
|
|
|
public void Update(float deltaTime)
|
|
{
|
|
processAfflictionChangesTimer -= deltaTime;
|
|
if (processAfflictionChangesTimer <= 0.0f)
|
|
{
|
|
foreach (var character in charactersWithAfflictionChanges)
|
|
{
|
|
ImmutableArray<NetAffliction> afflictions = GetAllAfflictions(character.CharacterHealth);
|
|
foreach (AfflictionSubscriber sub in afflictionSubscribers.ToList())
|
|
{
|
|
if (sub.Expiry < DateTimeOffset.Now)
|
|
{
|
|
afflictionSubscribers.Remove(sub);
|
|
continue;
|
|
}
|
|
|
|
if (sub.Target == character.Info)
|
|
{
|
|
ServerSend(new NetCrewMember(character.Info, afflictions),
|
|
header: NetworkHeader.AFFLICTION_UPDATE,
|
|
deliveryMethod: DeliveryMethod.Unreliable,
|
|
targetClient: sub.Subscriber);
|
|
}
|
|
}
|
|
}
|
|
charactersWithAfflictionChanges.Clear();
|
|
processAfflictionChangesTimer = ProcessAfflictionChangesInterval;
|
|
}
|
|
}
|
|
|
|
public void ServerRead(IReadMessage inc, Client sender)
|
|
{
|
|
NetworkHeader header = (NetworkHeader)inc.ReadByte();
|
|
|
|
switch (header)
|
|
{
|
|
case NetworkHeader.ADD_EVERYTHING_TO_PENDING:
|
|
ProcessAddEverything(sender);
|
|
break;
|
|
case NetworkHeader.UNSUBSCRIBE_ME:
|
|
RemoveClientSubscription(sender);
|
|
break;
|
|
case NetworkHeader.REQUEST_AFFLICTIONS:
|
|
ProcessRequestedAfflictions(inc, sender);
|
|
break;
|
|
case NetworkHeader.REQUEST_PENDING:
|
|
ProcessRequestedPending(sender);
|
|
break;
|
|
case NetworkHeader.ADD_PENDING:
|
|
ProcessNewAddition(inc, sender);
|
|
break;
|
|
case NetworkHeader.REMOVE_PENDING:
|
|
ProcessNewRemoval(inc, sender);
|
|
break;
|
|
case NetworkHeader.HEAL_PENDING:
|
|
ProcessHealing(sender);
|
|
break;
|
|
case NetworkHeader.CLEAR_PENDING:
|
|
ProcessClearing(sender);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void ProcessNewAddition(IReadMessage inc, Client client)
|
|
{
|
|
if (rateLimiter.IsLimitReached(client)) { return; }
|
|
|
|
NetCrewMember newCrewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
|
|
InsertPendingCrewMember(newCrewMember);
|
|
ServerSend(new NetCollection<NetCrewMember>(newCrewMember), NetworkHeader.ADD_PENDING, DeliveryMethod.Reliable, reponseClient: client);
|
|
}
|
|
|
|
private void ProcessAddEverything(Client client)
|
|
{
|
|
if (rateLimiter.IsLimitReached(client)) { return; }
|
|
AddEverythingToPending();
|
|
ServerSend(PendingHeals.ToNetCollection(), NetworkHeader.ADD_PENDING, DeliveryMethod.Reliable, reponseClient: client);
|
|
}
|
|
|
|
private void RemoveClientSubscription(Client client)
|
|
{
|
|
foreach (AfflictionSubscriber sub in afflictionSubscribers.ToList())
|
|
{
|
|
if (sub.Subscriber == client || sub.Expiry < DateTimeOffset.Now)
|
|
{
|
|
afflictionSubscribers.Remove(sub);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ProcessNewRemoval(IReadMessage inc, Client client)
|
|
{
|
|
if (rateLimiter.IsLimitReached(client)) { return; }
|
|
|
|
NetRemovedAffliction removed = INetSerializableStruct.Read<NetRemovedAffliction>(inc);
|
|
RemovePendingAffliction(removed.CrewMember, removed.Affliction);
|
|
ServerSend(removed, NetworkHeader.REMOVE_PENDING, DeliveryMethod.Reliable, reponseClient: client);
|
|
}
|
|
|
|
private void ProcessRequestedPending(Client client)
|
|
{
|
|
if (rateLimiter.IsLimitReached(client)) { return; }
|
|
|
|
ServerSend(PendingHeals.ToNetCollection(), NetworkHeader.REQUEST_PENDING, DeliveryMethod.Reliable, targetClient: client);
|
|
}
|
|
|
|
private void ProcessHealing(Client client)
|
|
{
|
|
if (rateLimiter.IsLimitReached(client)) { return; }
|
|
|
|
HealRequestResult result = HealAllPending(client: client);
|
|
ServerSend(new NetHealRequest { Result = result }, NetworkHeader.HEAL_PENDING, DeliveryMethod.Reliable, reponseClient: client);
|
|
}
|
|
|
|
private void ProcessClearing(Client client)
|
|
{
|
|
if (rateLimiter.IsLimitReached(client)) { return; }
|
|
|
|
if (!PendingHeals.Any()) { return; }
|
|
|
|
ClearPendingHeals();
|
|
ServerSend(null, NetworkHeader.CLEAR_PENDING, DeliveryMethod.Reliable, reponseClient: client);
|
|
}
|
|
|
|
private void ProcessRequestedAfflictions(IReadMessage inc, Client client)
|
|
{
|
|
if (rateLimiter.IsLimitReached(client)) { return; }
|
|
|
|
NetCrewMember crewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
|
|
|
|
CharacterInfo? foundInfo = crewMember.FindCharacterInfo(GetCrewCharacters());
|
|
|
|
ImmutableArray<NetAffliction> pendingAfflictions = ImmutableArray<NetAffliction>.Empty;
|
|
int infoId = 0;
|
|
|
|
if (foundInfo is null)
|
|
{
|
|
StringBuilder sb = new();
|
|
foreach (CharacterInfo character in GetCrewCharacters())
|
|
{
|
|
sb.AppendLine($" - {character.DisplayName} ({character.ID})");
|
|
}
|
|
|
|
DebugConsole.ThrowError($"Could not find the requested crew member with ID {crewMember.CharacterInfoID}.\n{sb}");
|
|
}
|
|
|
|
if (foundInfo is { Character.CharacterHealth: { } health })
|
|
{
|
|
pendingAfflictions = GetAllAfflictions(health);
|
|
infoId = foundInfo.ID;
|
|
}
|
|
|
|
INetSerializableStruct writeCrewMember = new NetCrewMember
|
|
{
|
|
CharacterInfoID = infoId,
|
|
Afflictions = pendingAfflictions
|
|
};
|
|
|
|
if (foundInfo is not null)
|
|
{
|
|
RemoveClientSubscription(client);
|
|
|
|
// the client subscribes to the afflictions of the crew member for the next minute
|
|
afflictionSubscribers.Add(new AfflictionSubscriber(client, foundInfo, DateTimeOffset.Now.AddMinutes(1)));
|
|
}
|
|
|
|
ServerSend(writeCrewMember, NetworkHeader.REQUEST_AFFLICTIONS, DeliveryMethod.Unreliable, client);
|
|
}
|
|
|
|
private IWriteMessage StartSending()
|
|
{
|
|
IWriteMessage msg = new WriteOnlyMessage();
|
|
msg.WriteByte((byte)ServerPacketHeader.MEDICAL);
|
|
return msg;
|
|
}
|
|
|
|
private void ServerSend(INetSerializableStruct? netStruct, NetworkHeader header, DeliveryMethod deliveryMethod, Client? targetClient = null, Client? reponseClient = null)
|
|
{
|
|
if (targetClient is null)
|
|
{
|
|
foreach (Client c in GameMain.Server.ConnectedClients)
|
|
{
|
|
SendToClient(c);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
SendToClient(targetClient);
|
|
|
|
void SendToClient(Client c)
|
|
{
|
|
MessageFlag flag = MessageFlag.Announce;
|
|
if (reponseClient != null && reponseClient == c)
|
|
{
|
|
flag = MessageFlag.Response;
|
|
}
|
|
|
|
IWriteMessage msg = StartSending();
|
|
msg.WriteByte((byte)header);
|
|
msg.WriteByte((byte)flag);
|
|
netStruct?.Write(msg);
|
|
GameMain.Server.ServerPeer.Send(msg, c.Connection, deliveryMethod);
|
|
}
|
|
}
|
|
}
|
|
} |