Add AntiAsshole and host display
This commit is contained in:
parent
01492a0c55
commit
bf22c17bba
@ -54,7 +54,7 @@ namespace RageCoop.Client
|
||||
|
||||
foreach (var player in Players.Values)
|
||||
{
|
||||
_mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Ping * 1000:N0}ms", player.Username, 116, 0, i - 1, "", "", 2, "", "", ' ');
|
||||
_mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Ping * 1000:N0}ms", player.Username+(player.IsHost ? " (Host)" : ""), 116, 0, i - 1, "", "", 2, "", "", ' ');
|
||||
}
|
||||
|
||||
_mainScaleform.CallFunction("SET_TITLE", "Player list", $"{Players.Count} players");
|
||||
@ -79,11 +79,11 @@ namespace RageCoop.Client
|
||||
public static void UpdatePlayer(Packets.PlayerInfoUpdate packet)
|
||||
{
|
||||
var p = GetPlayer(packet.PedID);
|
||||
if (p!=null && p.PedID!=Main.LocalPlayerID)
|
||||
if (p!=null)
|
||||
{
|
||||
p._latencyToServer = packet.Latency;
|
||||
p.Position = packet.Position;
|
||||
|
||||
p.IsHost= packet.IsHost;
|
||||
Main.QueueAction(() =>
|
||||
{
|
||||
if (p.FakeBlip?.Exists()!=true)
|
||||
@ -142,6 +142,7 @@ namespace RageCoop.Client
|
||||
internal class Player
|
||||
{
|
||||
public byte HolePunchStatus { get; set; } = 1;
|
||||
public bool IsHost;
|
||||
public string Username { get; internal set; }
|
||||
/// <summary>
|
||||
/// Universal character ID.
|
||||
|
@ -16,7 +16,7 @@ using System.Resources;
|
||||
|
||||
|
||||
// Version informationr(
|
||||
[assembly: AssemblyVersion("1.5.1.49")]
|
||||
[assembly: AssemblyFileVersion("1.5.1.49")]
|
||||
[assembly: AssemblyVersion("1.5.2.63")]
|
||||
[assembly: AssemblyFileVersion("1.5.2.63")]
|
||||
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]
|
||||
|
||||
|
@ -155,6 +155,11 @@ namespace RageCoop.Client
|
||||
}
|
||||
}
|
||||
|
||||
if (ped.IsInvincible)
|
||||
{
|
||||
flags |= PedDataFlags.IsInvincible;
|
||||
}
|
||||
|
||||
if (Function.Call<bool>(Hash.GET_PED_STEALTH_MOVEMENT, ped))
|
||||
{
|
||||
flags |= PedDataFlags.IsInStealthMode;
|
||||
|
@ -98,6 +98,7 @@ namespace RageCoop.Core
|
||||
IsInLowCover = 1 << 11,
|
||||
IsInCoverFacingLeft = 1 << 12,
|
||||
IsBlindFiring = 1 << 13,
|
||||
IsInvincible = 1 << 14,
|
||||
IsFullSync = 1<<15 ,
|
||||
}
|
||||
|
||||
|
@ -202,6 +202,7 @@ namespace RageCoop.Core
|
||||
public string Username { get; set; }
|
||||
public float Latency { get; set; }
|
||||
public Vector3 Position { get; set; }
|
||||
public bool IsHost;
|
||||
public override byte[] Serialize()
|
||||
{
|
||||
|
||||
@ -218,6 +219,8 @@ namespace RageCoop.Core
|
||||
|
||||
byteArray.AddVector3(Position);
|
||||
|
||||
byteArray.AddBool(IsHost);
|
||||
|
||||
return byteArray.ToArray();
|
||||
}
|
||||
|
||||
@ -234,6 +237,8 @@ namespace RageCoop.Core
|
||||
Latency=reader.ReadSingle();
|
||||
|
||||
Position=reader.ReadVector3();
|
||||
|
||||
IsHost=reader.ReadBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,8 @@ namespace RageCoop.Server
|
||||
PedID = c.Player.ID,
|
||||
Username = c.Username,
|
||||
Latency = c.Latency,
|
||||
Position = c.Player.Position
|
||||
Position = c.Player.Position,
|
||||
IsHost = c == _hostClient
|
||||
}.Pack(outgoingMessage);
|
||||
MainNetServer.SendToAll(outgoingMessage, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
|
||||
}
|
||||
@ -167,5 +168,16 @@ namespace RageCoop.Server
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
private void KickAssholes()
|
||||
{
|
||||
foreach(var c in ClientsByNetHandle.Values.ToArray())
|
||||
{
|
||||
if (c.EntitiesCount > 100 || c.Player.IsInvincible)
|
||||
{
|
||||
c.Kick("Bye bye asshole~");
|
||||
API.SendChatMessage($"Asshole {c.Username} was kicked!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
194
RageCoop.Server/Networking/Server.Listener.cs
Normal file
194
RageCoop.Server/Networking/Server.Listener.cs
Normal file
@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Core;
|
||||
using RageCoop.Core.Scripting;
|
||||
using RageCoop.Server.Scripting;
|
||||
|
||||
namespace RageCoop.Server
|
||||
{
|
||||
public partial class Server
|
||||
{
|
||||
private void Listen()
|
||||
{
|
||||
NetIncomingMessage msg = null;
|
||||
while (!_stopping)
|
||||
{
|
||||
try
|
||||
{
|
||||
msg = MainNetServer.WaitMessage(200);
|
||||
ProcessMessage(msg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger?.Error("Error processing message");
|
||||
Logger?.Error(ex);
|
||||
if (msg != null)
|
||||
{
|
||||
DisconnectAndLog(msg.SenderConnection, PacketType.Unknown, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger?.Info("Server is shutting down!");
|
||||
MainNetServer.Shutdown("Server is shutting down!");
|
||||
BaseScript.OnStop();
|
||||
Resources.UnloadAll();
|
||||
}
|
||||
|
||||
private void ProcessMessage(NetIncomingMessage message)
|
||||
{
|
||||
Client sender;
|
||||
if (message == null) { return; }
|
||||
switch (message.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.ConnectionApproval:
|
||||
{
|
||||
Logger?.Info($"New incoming connection from: [{message.SenderConnection.RemoteEndPoint}]");
|
||||
if (message.ReadByte() != (byte)PacketType.Handshake)
|
||||
{
|
||||
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: Wrong packet!");
|
||||
message.SenderConnection.Deny("Wrong packet!");
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
int len = message.ReadInt32();
|
||||
byte[] data = message.ReadBytes(len);
|
||||
GetHandshake(message.SenderConnection, data.GetPacket<Packets.Handshake>());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: {e.Message}");
|
||||
Logger?.Error(e);
|
||||
message.SenderConnection.Deny(e.Message);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
{
|
||||
// Get sender client
|
||||
if (!ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
|
||||
{
|
||||
break;
|
||||
}
|
||||
NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
|
||||
|
||||
if (status == NetConnectionStatus.Disconnected)
|
||||
{
|
||||
|
||||
PlayerDisconnected(sender);
|
||||
}
|
||||
else if (status == NetConnectionStatus.Connected)
|
||||
{
|
||||
PlayerConnected(sender);
|
||||
_worker.QueueJob(() => API.Events.InvokePlayerConnected(sender));
|
||||
Resources.SendTo(sender);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NetIncomingMessageType.Data:
|
||||
{
|
||||
|
||||
// Get sender client
|
||||
if (ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
|
||||
{
|
||||
// Get packet type
|
||||
var type = (PacketType)message.ReadByte();
|
||||
switch (type)
|
||||
{
|
||||
case PacketType.Response:
|
||||
{
|
||||
int id = message.ReadInt32();
|
||||
if (PendingResponses.TryGetValue(id, out var callback))
|
||||
{
|
||||
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32()));
|
||||
PendingResponses.Remove(id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PacketType.Request:
|
||||
{
|
||||
int id = message.ReadInt32();
|
||||
if (RequestHandlers.TryGetValue((PacketType)message.ReadByte(), out var handler))
|
||||
{
|
||||
var response = MainNetServer.CreateMessage();
|
||||
response.Write((byte)PacketType.Response);
|
||||
response.Write(id);
|
||||
handler(message.ReadBytes(message.ReadInt32()), sender).Pack(response);
|
||||
MainNetServer.SendMessage(response, message.SenderConnection, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
byte[] data = message.ReadBytes(message.ReadInt32());
|
||||
if (type.IsSyncEvent())
|
||||
{
|
||||
// Sync Events
|
||||
|
||||
if (Settings.UseP2P) { break; }
|
||||
try
|
||||
{
|
||||
var toSend = MainNetServer.Connections.Exclude(message.SenderConnection);
|
||||
if (toSend.Count != 0)
|
||||
{
|
||||
var outgoingMessage = MainNetServer.CreateMessage();
|
||||
outgoingMessage.Write((byte)type);
|
||||
outgoingMessage.Write(data.Length);
|
||||
outgoingMessage.Write(data);
|
||||
MainNetServer.SendMessage(outgoingMessage, toSend, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.SyncEvents);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DisconnectAndLog(message.SenderConnection, type, e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HandlePacket(type, data, sender);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
Logger?.Error(message.ReadString());
|
||||
break;
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
Logger?.Warning(message.ReadString());
|
||||
break;
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
Logger?.Debug(message.ReadString());
|
||||
break;
|
||||
case NetIncomingMessageType.UnconnectedData:
|
||||
{
|
||||
if (message.ReadByte() == (byte)PacketType.PublicKeyRequest)
|
||||
{
|
||||
var msg = MainNetServer.CreateMessage();
|
||||
var p = new Packets.PublicKeyResponse();
|
||||
Security.GetPublicKey(out p.Modulus, out p.Exponent);
|
||||
p.Pack(msg);
|
||||
Logger?.Debug($"Sending public key to {message.SenderEndPoint}, length:{msg.LengthBytes}");
|
||||
MainNetServer.SendUnconnectedMessage(msg, message.SenderEndPoint);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Logger?.Error(string.Format("Unhandled type: {0} {1} bytes {2} | {3}", message.MessageType, message.LengthBytes, message.DeliveryMethod, message.SequenceChannel));
|
||||
break;
|
||||
}
|
||||
|
||||
MainNetServer.Recycle(message);
|
||||
}
|
||||
}
|
||||
}
|
@ -50,6 +50,7 @@ namespace RageCoop.Server
|
||||
private readonly Thread _listenerThread;
|
||||
private readonly Timer _announceTimer = new();
|
||||
private readonly Timer _playerUpdateTimer = new();
|
||||
private readonly Timer _antiAssholesTimer = new();
|
||||
private readonly Timer _updateTimer = new();
|
||||
private readonly Worker _worker;
|
||||
private readonly HashSet<char> _allowedCharacterSet;
|
||||
@ -95,7 +96,11 @@ namespace RageCoop.Server
|
||||
_playerUpdateTimer.Interval = 1000;
|
||||
_playerUpdateTimer.Elapsed += (s, e) => SendPlayerUpdate();
|
||||
|
||||
|
||||
|
||||
_antiAssholesTimer.Interval = 5000;
|
||||
_antiAssholesTimer.Elapsed += (s, e) => KickAssholes();
|
||||
|
||||
|
||||
_updateTimer.Interval = 1;
|
||||
_updateTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
@ -161,6 +166,10 @@ namespace RageCoop.Server
|
||||
{
|
||||
_updateTimer.Enabled = true;
|
||||
}
|
||||
if (Settings.AntiAssholes)
|
||||
{
|
||||
_antiAssholesTimer.Enabled = true;
|
||||
}
|
||||
|
||||
Logger?.Info("Listening for clients");
|
||||
}
|
||||
@ -177,184 +186,6 @@ namespace RageCoop.Server
|
||||
_announceTimer.Enabled = false;
|
||||
_worker.Dispose();
|
||||
}
|
||||
private void Listen()
|
||||
{
|
||||
NetIncomingMessage msg=null;
|
||||
while (!_stopping)
|
||||
{
|
||||
try
|
||||
{
|
||||
msg=MainNetServer.WaitMessage(200);
|
||||
ProcessMessage(msg);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Logger?.Error("Error processing message");
|
||||
Logger?.Error(ex);
|
||||
if (msg!=null)
|
||||
{
|
||||
DisconnectAndLog(msg.SenderConnection, PacketType.Unknown, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger?.Info("Server is shutting down!");
|
||||
MainNetServer.Shutdown("Server is shutting down!");
|
||||
BaseScript.OnStop();
|
||||
Resources.UnloadAll();
|
||||
}
|
||||
|
||||
private void ProcessMessage(NetIncomingMessage message)
|
||||
{
|
||||
Client sender;
|
||||
if (message == null) { return; }
|
||||
switch (message.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.ConnectionApproval:
|
||||
{
|
||||
Logger?.Info($"New incoming connection from: [{message.SenderConnection.RemoteEndPoint}]");
|
||||
if (message.ReadByte() != (byte)PacketType.Handshake)
|
||||
{
|
||||
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: Wrong packet!");
|
||||
message.SenderConnection.Deny("Wrong packet!");
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
int len = message.ReadInt32();
|
||||
byte[] data = message.ReadBytes(len);
|
||||
GetHandshake(message.SenderConnection, data.GetPacket<Packets.Handshake>());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: {e.Message}");
|
||||
Logger?.Error(e);
|
||||
message.SenderConnection.Deny(e.Message);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
{
|
||||
// Get sender client
|
||||
if (!ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
|
||||
{
|
||||
break;
|
||||
}
|
||||
NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
|
||||
|
||||
if (status == NetConnectionStatus.Disconnected)
|
||||
{
|
||||
|
||||
PlayerDisconnected(sender);
|
||||
}
|
||||
else if (status == NetConnectionStatus.Connected)
|
||||
{
|
||||
PlayerConnected(sender);
|
||||
_worker.QueueJob(() => API.Events.InvokePlayerConnected(sender));
|
||||
Resources.SendTo(sender);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NetIncomingMessageType.Data:
|
||||
{
|
||||
|
||||
// Get sender client
|
||||
if (ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
|
||||
{
|
||||
// Get packet type
|
||||
var type = (PacketType)message.ReadByte();
|
||||
switch (type)
|
||||
{
|
||||
case PacketType.Response:
|
||||
{
|
||||
int id = message.ReadInt32();
|
||||
if (PendingResponses.TryGetValue(id, out var callback))
|
||||
{
|
||||
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32()));
|
||||
PendingResponses.Remove(id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PacketType.Request:
|
||||
{
|
||||
int id = message.ReadInt32();
|
||||
if (RequestHandlers.TryGetValue((PacketType)message.ReadByte(), out var handler))
|
||||
{
|
||||
var response=MainNetServer.CreateMessage();
|
||||
response.Write((byte)PacketType.Response);
|
||||
response.Write(id);
|
||||
handler(message.ReadBytes(message.ReadInt32()),sender).Pack(response);
|
||||
MainNetServer.SendMessage(response,message.SenderConnection,NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
byte[] data = message.ReadBytes(message.ReadInt32());
|
||||
if (type.IsSyncEvent())
|
||||
{
|
||||
// Sync Events
|
||||
|
||||
if (Settings.UseP2P) { break; }
|
||||
try
|
||||
{
|
||||
var toSend = MainNetServer.Connections.Exclude(message.SenderConnection);
|
||||
if (toSend.Count!=0)
|
||||
{
|
||||
var outgoingMessage = MainNetServer.CreateMessage();
|
||||
outgoingMessage.Write((byte)type);
|
||||
outgoingMessage.Write(data.Length);
|
||||
outgoingMessage.Write(data);
|
||||
MainNetServer.SendMessage(outgoingMessage, toSend, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.SyncEvents);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DisconnectAndLog(message.SenderConnection, type, e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HandlePacket(type, data, sender);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
Logger?.Error(message.ReadString());
|
||||
break;
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
Logger?.Warning(message.ReadString());
|
||||
break;
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
Logger?.Debug(message.ReadString());
|
||||
break;
|
||||
case NetIncomingMessageType.UnconnectedData:
|
||||
{
|
||||
if (message.ReadByte()==(byte)PacketType.PublicKeyRequest)
|
||||
{
|
||||
var msg = MainNetServer.CreateMessage();
|
||||
var p=new Packets.PublicKeyResponse();
|
||||
Security.GetPublicKey(out p.Modulus,out p.Exponent);
|
||||
p.Pack(msg);
|
||||
Logger?.Debug($"Sending public key to {message.SenderEndPoint}, length:{msg.LengthBytes}");
|
||||
MainNetServer.SendUnconnectedMessage(msg, message.SenderEndPoint);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Logger?.Error(string.Format("Unhandled type: {0} {1} bytes {2} | {3}", message.MessageType, message.LengthBytes, message.DeliveryMethod, message.SequenceChannel));
|
||||
break;
|
||||
}
|
||||
|
||||
MainNetServer.Recycle(message);
|
||||
}
|
||||
internal void QueueJob(Action job)
|
||||
{
|
||||
_worker.QueueJob(job);
|
||||
|
@ -15,7 +15,7 @@ using System.Resources;
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information
|
||||
[assembly: AssemblyVersion("1.5.1.69")]
|
||||
[assembly: AssemblyFileVersion("1.5.1.69")]
|
||||
[assembly: AssemblyVersion("1.5.2.77")]
|
||||
[assembly: AssemblyFileVersion("1.5.2.77")]
|
||||
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]
|
||||
|
||||
|
@ -211,6 +211,7 @@ namespace RageCoop.Server.Scripting
|
||||
ped.Owner=sender;
|
||||
ped.Health=p.Health;
|
||||
ped._rot=p.Rotation;
|
||||
ped._isInvincible = p.Flags.HasPedFlag(PedDataFlags.IsInvincible);
|
||||
if (p.Speed>=4 && Vehicles.TryGetValue(p.VehicleID,out var v))
|
||||
{
|
||||
ped.LastVehicle=v;
|
||||
@ -270,7 +271,7 @@ namespace RageCoop.Server.Scripting
|
||||
internal void RemoveVehicle(int id)
|
||||
{
|
||||
Vehicles.TryRemove(id, out var veh);
|
||||
if (veh.Owner != null)
|
||||
if (veh?.Owner != null)
|
||||
{
|
||||
veh.Owner.EntitiesCount--;
|
||||
}
|
||||
@ -287,7 +288,7 @@ namespace RageCoop.Server.Scripting
|
||||
internal void RemovePed(int id)
|
||||
{
|
||||
Peds.TryRemove(id, out var ped);
|
||||
if (ped.Owner != null)
|
||||
if (ped?.Owner != null)
|
||||
{
|
||||
ped.Owner.EntitiesCount--;
|
||||
}
|
||||
|
@ -192,6 +192,16 @@ namespace RageCoop.Server.Scripting
|
||||
/// Health
|
||||
/// </summary>
|
||||
public int Health { get; internal set; }
|
||||
|
||||
|
||||
internal bool _isInvincible;
|
||||
/// <summary>
|
||||
/// Get or set whether this ped is invincible
|
||||
/// </summary>
|
||||
public bool IsInvincible {
|
||||
get => _isInvincible;
|
||||
set => Owner.SendNativeCall(Hash.SET_ENTITY_INVINCIBLE,Handle,value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents a vehicle from a client
|
||||
|
@ -109,11 +109,10 @@
|
||||
/// Automatically update to nightly build when an update is avalible, check is performed every 10 minutes.
|
||||
/// </summary>
|
||||
public bool AutoUpdate { get; set; } = false;
|
||||
/*
|
||||
|
||||
/// <summary>
|
||||
/// Kick godmode and spamming idiots
|
||||
/// Kick godmode and spamming assholes
|
||||
/// </summary>
|
||||
public bool AntiIdiots { get; set; } = true;
|
||||
*/
|
||||
public bool AntiAssholes { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user