private void SendKarmaNotifications(Client client, string debugKarmaChangeReason = "") { //send a notification about karma changing if the karma has changed by x% var clientMemory = GetClientMemory(client); float karmaChange = client.Karma - clientMemory.PreviousNotifiedKarma; if (Math.Abs(karmaChange) > 1.0f && TestMode) { string msg = karmaChange < 0 ? $"Your karma has decreased to {client.Karma}" : $"Your karma has increased to {client.Karma}"; if (!string.IsNullOrEmpty(debugKarmaChangeReason)) { msg += $". Reason: {debugKarmaChangeReason}"; } GameMain.Server.SendDirectChatMessage(msg, client); clientMemory.PreviousNotifiedKarma = client.Karma; clientMemory.PreviousKarmaNotificationTime = Timing.TotalTime; } else if (Timing.TotalTime >= clientMemory.PreviousKarmaNotificationTime + 5.0f && clientMemory.PreviousNotifiedKarma >= KickBanThreshold + KarmaNotificationInterval && client.Karma < KickBanThreshold + KarmaNotificationInterval) { GameMain.Server.SendDirectChatMessage(TextManager.Get("KarmaBanWarning"), client); GameServer.Log(GameServer.ClientLogName(client) + " has been warned for having dangerously low karma.", ServerLog.MessageType.Karma); clientMemory.PreviousNotifiedKarma = client.Karma; clientMemory.PreviousKarmaNotificationTime = Timing.TotalTime; } }
private void AdjustKarma(Character target, float amount, string debugKarmaChangeReason = "") { if (target == null) { return; } Client client = GameMain.Server.ConnectedClients.Find(c => c.Character == target); if (client == null) { return; } //all penalties/rewards are halved when wearing a clown costume if (target.HasEquippedItem("clownmask") && target.HasEquippedItem("clowncostume")) { amount *= 0.5f; } client.Karma += amount; if (amount < 0.0f) { float?herpesStrength = client.Character?.CharacterHealth.GetAfflictionStrength("spaceherpes"); var clientMemory = GetClientMemory(client); clientMemory.KarmaDecreasesInPastMinute.RemoveAll(ta => ta.Time + 60.0f < Timing.TotalTime); float aggregate = clientMemory.KarmaDecreasesInPastMinute.Select(ta => ta.Amount).DefaultIfEmpty().Aggregate((a, b) => a + b); clientMemory.KarmaDecreasesInPastMinute.Add(new ClientMemory.TimeAmount() { Time = Timing.TotalTime, Amount = -amount }); if (herpesStrength.HasValue && herpesStrength <= 0.0f && aggregate - amount > 25.0f && aggregate <= 25.0f) { GameServer.Log($"{GameServer.ClientLogName(client)} has lost more than 25 karma in the past minute.", ServerLog.MessageType.Karma); } } if (TestMode) { SendKarmaNotifications(client, debugKarmaChangeReason); } }
private void UpdateClient(Client client, float deltaTime) { if (client.Character != null && !client.Character.Removed && !client.Character.IsDead) { if (client.Karma > KarmaDecayThreshold) { client.Karma -= KarmaDecay * deltaTime; } else if (client.Karma < KarmaIncreaseThreshold) { client.Karma += KarmaIncrease * deltaTime; } //increase the strength of the herpes affliction in steps instead of linearly //otherwise clients could determine their exact karma value from the strength float herpesStrength = 0.0f; if (client.Karma < 20) { herpesStrength = 100.0f; } else if (client.Karma < 30) { herpesStrength = 60.0f; } else if (client.Karma < 40.0f) { herpesStrength = 30.0f; } var existingAffliction = client.Character.CharacterHealth.GetAffliction <AfflictionSpaceHerpes>("spaceherpes"); if (existingAffliction == null && herpesStrength > 0.0f) { client.Character.CharacterHealth.ApplyAffliction(null, new Affliction(herpesAffliction, herpesStrength)); GameServer.Log($"{GameServer.ClientLogName(client)} has contracted space herpes due to low karma.", ServerLog.MessageType.Karma); GameMain.NetworkMember.LastClientListUpdateID++; } else if (existingAffliction != null) { existingAffliction.Strength = herpesStrength; if (herpesStrength <= 0.0f) { client.Character.CharacterHealth.ReduceAffliction(null, "invertcontrols", 100.0f); } } //check if the client has disconnected an excessive number of wires var clientMemory = GetClientMemory(client); if (clientMemory.WireDisconnectTime.Count > (int)AllowedWireDisconnectionsPerMinute) { clientMemory.WireDisconnectTime.RemoveRange(0, clientMemory.WireDisconnectTime.Count - (int)AllowedWireDisconnectionsPerMinute); if (clientMemory.WireDisconnectTime.All(w => Timing.TotalTime - w.Second < 60.0f)) { float karmaDecrease = -WireDisconnectionKarmaDecrease; //engineers don't lose as much karma for removing lots of wires if (client.Character.Info?.Job.Prefab.Identifier == "engineer") { karmaDecrease *= 0.5f; } AdjustKarma(client.Character, karmaDecrease, "Disconnected excessive number of wires"); } } if (client.Character?.Info?.Job.Prefab.Identifier == "captain" && client.Character.SelectedConstruction != null) { if (client.Character.SelectedConstruction.GetComponent <Steering>() != null) { AdjustKarma(client.Character, SteerSubKarmaIncrease * deltaTime, "Steering the sub"); } } } if (client.Karma < KickBanThreshold && client.Connection != GameMain.Server.OwnerConnection) { if (TestMode) { client.Karma = 50.0f; GameMain.Server.SendDirectChatMessage("BANNED! (not really because karma test mode is enabled)", client); } else { bannedClients.Add(client); } } }
public void ServerRead(IReadMessage inc, Client sender) { if (GameMain.Server == null || sender == null) { return; } byte voteTypeByte = inc.ReadByte(); VoteType voteType = VoteType.Unknown; try { voteType = (VoteType)voteTypeByte; } catch (Exception e) { DebugConsole.ThrowError("Failed to cast vote type \"" + voteTypeByte + "\"", e); return; } switch (voteType) { case VoteType.Sub: string subName = inc.ReadString(); SubmarineInfo sub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName); sender.SetVote(voteType, sub); break; case VoteType.Mode: string modeIdentifier = inc.ReadString(); GameModePreset mode = GameModePreset.List.Find(gm => gm.Identifier == modeIdentifier); if (!mode.Votable) { break; } sender.SetVote(voteType, mode); break; case VoteType.EndRound: if (!sender.HasSpawned) { return; } sender.SetVote(voteType, inc.ReadBoolean()); GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote <bool>(VoteType.EndRound)); GameMain.NetworkMember.EndVoteMax = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned); break; case VoteType.Kick: byte kickedClientID = inc.ReadByte(); Client kicked = GameMain.Server.ConnectedClients.Find(c => c.ID == kickedClientID); if (kicked != null && kicked.Connection != GameMain.Server.OwnerConnection && !kicked.HasKickVoteFrom(sender)) { kicked.AddKickVote(sender); Client.UpdateKickVotes(GameMain.Server.ConnectedClients); GameMain.Server.SendChatMessage($"ServerMessage.HasVotedToKick~[initiator]={sender.Name}~[target]={kicked.Name}", ChatMessageType.Server, null); } break; case VoteType.StartRound: bool ready = inc.ReadBoolean(); if (ready != sender.GetVote <bool>(VoteType.StartRound)) { sender.SetVote(VoteType.StartRound, ready); GameServer.Log(GameServer.ClientLogName(sender) + (ready ? " is ready to start the game." : " is not ready to start the game."), ServerLog.MessageType.ServerMessage); } break; case VoteType.PurchaseAndSwitchSub: case VoteType.PurchaseSub: case VoteType.SwitchSub: bool startVote = inc.ReadBoolean(); if (startVote) { StartSubmarineVote(inc, voteType, sender); } else { sender.SetVote(voteType, (int)inc.ReadByte()); } GameMain.Server.SubmarineVoteYesCount = GameMain.Server.ConnectedClients.Count(c => c.GetVote <int>(SubVote.VoteType) == 2); GameMain.Server.SubmarineVoteNoCount = GameMain.Server.ConnectedClients.Count(c => c.GetVote <int>(SubVote.VoteType) == 1); GameMain.Server.SubmarineVoteMax = GameMain.Server.ConnectedClients.Count(c => c.InGame); break; } inc.ReadPadBits(); GameMain.Server.UpdateVoteStatus(); }