Browse Source

Implement server-side chat and chat command framework

tags/v0.0.3
NGnius (Graham) 2 years ago
parent
commit
b8a8a535f1
17 changed files with 783 additions and 7 deletions
  1. +1
    -1
      CLre/CLre.cs
  2. +16
    -0
      CLre_server/API/Config/CLreConfig.cs
  3. +68
    -0
      CLre_server/API/MainServer/ModerationEngine.cs
  4. +139
    -0
      CLre_server/API/MainServer/Moderator.cs
  5. +19
    -2
      CLre_server/API/MainServer/Server.cs
  6. +27
    -0
      CLre_server/API/MainServer/ServerEngines.cs
  7. +61
    -0
      CLre_server/API/Utility/CardLifeUserAuthentication.cs
  8. +8
    -2
      CLre_server/CLre_server.cs
  9. +60
    -0
      CLre_server/Tweaks/Chat/Attributes.cs
  10. +21
    -0
      CLre_server/Tweaks/Chat/AuthenticationEngine.cs
  11. +12
    -0
      CLre_server/Tweaks/Chat/ChatConfig.cs
  12. +88
    -0
      CLre_server/Tweaks/Chat/ChatConnectionEngine.cs
  13. +33
    -0
      CLre_server/Tweaks/Chat/ChatHandler.cs
  14. +116
    -0
      CLre_server/Tweaks/Chat/ChatListener.cs
  15. +111
    -0
      CLre_server/Tweaks/Chat/ModeratorCommands.cs
  16. +2
    -1
      CLre_server/WebStatus/Attributes.cs
  17. +1
    -1
      CLre_server/WebStatus/WebServer.cs

+ 1
- 1
CLre/CLre.cs View File

@@ -18,7 +18,7 @@ namespace CLre
{
public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;

public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
public override string Version { get; } = "21Q3 " + Assembly.GetExecutingAssembly().GetName().Version.ToString();

internal static Harmony harmonyInstance = null;



+ 16
- 0
CLre_server/API/Config/CLreConfig.cs View File

@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Text;

namespace CLre_server.API.Config
{
@@ -9,6 +10,11 @@ namespace CLre_server.API.Config
public bool clre_clients_only;
public bool web_server;
public bool terrain_exclusion_zone;
public bool chat_commands;
public string email_address;
public string password;
public string[] bans;
public string[] moderators;

public static CLreConfig Default()
{
@@ -17,6 +23,11 @@ namespace CLre_server.API.Config
clre_clients_only = false,
web_server = false,
terrain_exclusion_zone = false,
chat_commands = false,
email_address = "email@address.com",
password = "s3cur3-password",
bans = new string[0],
moderators = new []{ "NGuiness", "NGnius", "Zhang"},
};
}

@@ -60,5 +71,10 @@ namespace CLre_server.API.Config
{
return UnityEngine.JsonUtility.ToJson(this, true);
}

public void ToFile(string path)
{
File.WriteAllText(path, this.ToString());
}
}
}

+ 68
- 0
CLre_server/API/MainServer/ModerationEngine.cs View File

@@ -0,0 +1,68 @@
using System;
using System.Reflection;
using HarmonyLib;
using Svelto.Context;
using Svelto.DataStructures;
using Svelto.ECS;
using User.Server;

namespace CLre_server.API.MainServer
{
class ModerationEngine : Engines.ServerEnginePostBuild
{
public override void Ready()
{
}

public int? FindConnectedPlayerById(string publicId)
{
FieldInfo f = AccessTools.Field(AccessTools.TypeByName("User.Server.AccountExclusiveGroups"), "accountGroup");
ExclusiveGroup accountGroup = (ExclusiveGroup) f.GetValue(null);
ReadOnlyCollectionStruct<AccountIdServerNode> accounts =
entitiesDB.QueryEntityViews<AccountIdServerNode>(accountGroup);
for (int i = 0; i < accounts.Count; i++)
{
if (accounts[i].accountId.publicId.ToString() == publicId)
{
return i;
}
}

return null;
}

public int? FindConnectedPlayerByName(string displayName)
{
FieldInfo f = AccessTools.Field(AccessTools.TypeByName("User.Server.AccountExclusiveGroups"), "accountGroup");
ExclusiveGroup accountGroup = (ExclusiveGroup) f.GetValue(null);
ReadOnlyCollectionStruct<AccountIdServerNode> accounts =
entitiesDB.QueryEntityViews<AccountIdServerNode>(accountGroup);
for (int i = 0; i < accounts.Count; i++)
{
if (String.Equals(accounts[i].accountId.displayName, displayName, StringComparison.InvariantCultureIgnoreCase))
{
return i;
}
}

return null;
}

public Guid? FindConnectedPlayerGuidByName(string displayName)
{
FieldInfo f = AccessTools.Field(AccessTools.TypeByName("User.Server.AccountExclusiveGroups"), "accountGroup");
ExclusiveGroup accountGroup = (ExclusiveGroup) f.GetValue(null);
ReadOnlyCollectionStruct<AccountIdServerNode> accounts =
entitiesDB.QueryEntityViews<AccountIdServerNode>(accountGroup);
for (int i = 0; i < accounts.Count; i++)
{
if (String.Equals(accounts[i].accountId.displayName, displayName, StringComparison.InvariantCultureIgnoreCase))
{
return accounts[i].accountId.publicId;
}
}

return null;
}
}
}

+ 139
- 0
CLre_server/API/MainServer/Moderator.cs View File

@@ -0,0 +1,139 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using GameNetworkLayer.Shared;

namespace CLre_server.API.MainServer
{
public class Moderator
{
private static Moderator _instance = null;

public static Moderator Instance
{
get
{
if (_instance == null) Init();
return _instance;
}
}

internal static void Init()
{
if (_instance == null) _instance = new Moderator();
}

private ModerationEngine _moderationEngine;

private Moderator()
{
_moderationEngine = new ModerationEngine();
Server.Instance.PlayerConnect += (sender, args) =>
{
#if DEBUG
Utility.Logging.MetaLog($"Player {args.PlayerId} is connecting, starting ban checker");
#endif
CheckConnectingPlayerAsap(args.PlayerId).Run();
};
}

public bool DisconnectPlayerById(string publicId)
{
int? playerId = _moderationEngine.FindConnectedPlayerById(publicId);
if (playerId.HasValue)
{
UserVerification.Instance.DisconnectPlayer(playerId.Value, NetworkDispatcherCode.GameDataVerificationFail);
return true;
}

return false;
}
public bool DisconnectPlayerByName(string name)
{
int? playerId = _moderationEngine.FindConnectedPlayerByName(name);
if (playerId.HasValue)
{
UserVerification.Instance.DisconnectPlayer(playerId.Value, NetworkDispatcherCode.GameDataVerificationFail);
return true;
}

return false;
}
public bool BanPlayerById(string publicId)
{
List<string> bans = new List<string>(CLre.Config.bans);
if (!bans.Contains(publicId))
{
bans.Add(publicId);
CLre.Config.bans = bans.ToArray();
}
return DisconnectPlayerById(publicId);
}

public bool BanPlayerByName(string name)
{
List<string> bans = new List<string>(CLre.Config.bans);
if (!bans.Contains(name))
{
bans.Add(name);
CLre.Config.bans = bans.ToArray();
}
return DisconnectPlayerByName(name);
}

public bool IsModerator(string name)
{
foreach (string modName in CLre.Config.moderators)
{
if (string.Compare(name, modName, StringComparison.InvariantCultureIgnoreCase) == 0)
{
return true;
}
}

Guid? publicId = _moderationEngine.FindConnectedPlayerGuidByName(name);
if (publicId.HasValue)
{
foreach (string modGuid in CLre.Config.moderators)
{
if (modGuid == publicId.ToString())
{
return true;
}
}
}
return false;
}
public IEnumerator CheckConnectingPlayerAsap(int playerId)
{
while (Server.Instance.Players.Length <= playerId)
{
yield return null;
yield return null;
yield return null;
yield return null;
}
var connector = Server.Instance.Players[playerId];
if (CLre.Config.bans.Contains(connector.accountId.displayName)
|| CLre.Config.bans.Contains(connector.accountId.publicId.ToString()))
{
#if DEBUG
Utility.Logging.MetaLog($"Banned player {connector.accountId.displayName} ({connector.accountId.publicId}) tried to connect, kicking");
#endif
UserVerification.Instance.DisconnectPlayer(playerId, NetworkDispatcherCode.GameDataVerificationFail);
}
#if DEBUG
else
{
Utility.Logging.MetaLog($"Player {connector.accountId.displayName} ({connector.accountId.publicId}) is not banned, skipping auto-kick");
}
#endif
}
}
}

+ 19
- 2
CLre_server/API/MainServer/Server.cs View File

@@ -5,6 +5,7 @@ using GameServer;
using HarmonyLib;
using NetworkFramework.Server;
using Svelto.Context;
using User.Server;

namespace CLre_server.API.MainServer
{
@@ -18,10 +19,15 @@ namespace CLre_server.API.MainServer
{
get
{
if (_instance == null) _instance = new Server();
if (_instance == null) Init();
return _instance;
}
}

internal static void Init()
{
if (_instance == null) _instance = new Server();
}
// instance events
public event EventHandler<StartingEventArgs> InitStart
@@ -79,9 +85,20 @@ namespace CLre_server.API.MainServer
}
}

public AccountIdServerNode[] Players
{
get => _serverDatabaseQueryEngine.GetConnectedAccounts();
}
// fields
private ServerDatabaseQueryEngine _serverDatabaseQueryEngine;
private ServerReadyEngine _serverReadyEngine;

private Server()
{
new ServerReadyEngine();
_serverReadyEngine = new ServerReadyEngine();
_serverDatabaseQueryEngine = new ServerDatabaseQueryEngine();
}
}


+ 27
- 0
CLre_server/API/MainServer/ServerEngines.cs View File

@@ -1,10 +1,16 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using CLre_server.API.Engines;
using Game.DataLoader;
using GameServer;
using HarmonyLib;
using Svelto.Context;
using Svelto.DataStructures;
using Svelto.ECS;
using User.Server;

namespace CLre_server.API.MainServer
{
@@ -45,4 +51,25 @@ namespace CLre_server.API.MainServer
if (serverFrameworkDestroyed != null) serverFrameworkDestroyed(this, new StopEventArgs{});
}
}

class ServerDatabaseQueryEngine : ServerEnginePostBuild
{
public override void Ready()
{
}

public AccountIdServerNode[] GetConnectedAccounts()
{
FieldInfo f = AccessTools.Field(AccessTools.TypeByName("User.Server.AccountExclusiveGroups"), "accountGroup");
ExclusiveGroup accountGroup = (ExclusiveGroup) f.GetValue(null);
ReadOnlyCollectionStruct<AccountIdServerNode> accounts =
entitiesDB.QueryEntityViews<AccountIdServerNode>(accountGroup);
List<AccountIdServerNode> list = new List<AccountIdServerNode>();
foreach (var a in accounts)
{
list.Add(a);
}
return list.ToArray();
}
}
}

+ 61
- 0
CLre_server/API/Utility/CardLifeUserAuthentication.cs View File

@@ -0,0 +1,61 @@
using System;
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;

namespace CLre_server.API.Utility
{
public static class CardLifeUserAuthentication
{
[Serializable]
private struct AuthPayload
{
public string EmailAddress;
public string Password;
}

private const string LOGIN_URL = "https://live-auth.cardlifegame.com/api/auth/authenticate";

public delegate void OnResponse(AuthenticationResponse data);
public static IEnumerator Authenticate(string email, string password, OnResponse then)
{
UnityWebRequest req = new UnityWebRequest(LOGIN_URL, "POST");
AuthPayload payload = new AuthPayload
{
EmailAddress = email,
Password = password,
};
byte[] bytes = Encoding.UTF8.GetBytes(JsonUtility.ToJson(payload));
req.uploadHandler = new UploadHandlerRaw(bytes);
req.downloadHandler = new DownloadHandlerBuffer();
req.SetRequestHeader("Content-Type", "application/json");
AsyncOperation op = req.SendWebRequest();
while (!op.isDone)
{
yield return null;
}

if (req.responseCode != 200)
{
Logging.LogError($"Authentication with email {email} returned code {req.responseCode} and was aborted. Response:\n{req.downloadHandler.text}");
yield break;
}

AuthenticationResponse resp = JsonUtility.FromJson<AuthenticationResponse>(req.downloadHandler.text);
then(resp);
}
}

[Serializable]
public struct AuthenticationResponse
{
public string PublicId;
public string EmailAddress;
public string DisplayName;
public bool Confirmed;
public string Token;
public uint ID;
}
}

+ 8
- 2
CLre_server/CLre_server.cs View File

@@ -18,15 +18,18 @@ namespace CLre_server
{
public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;

public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
public override string Version { get; } = "21Q3 " + Assembly.GetExecutingAssembly().GetName().Version.ToString();

internal static Harmony harmonyInstance = null;

private const string CONFIG_PATH = "CLre_server.json";
public static CLreConfig Config = CLreConfig.Default();

// called when Cardlife shuts down
public override void OnApplicationQuit()
{
Config.ToFile(CONFIG_PATH);
WebServer.Deinit();
harmonyInstance.UnpatchAll();
}
@@ -88,11 +91,14 @@ namespace CLre_server
API.MainServer.Server.Instance.InitComplete += (_, __) => API.Utility.Logging.MetaLog("(!) Server successfully initialised");
#endif
// try to load config file
Config = CLreConfig.FromFileSafely("CLre_server.json");
Config = CLreConfig.FromFileSafely(CONFIG_PATH);
// init config-dependent functionality
WebServer.Init();
API.Synergy.CLreEnforcer.Init();
Tweaks.TerrainModificationExclusionZone.Init();
Tweaks.Chat.ChatHandler.Init();
API.MainServer.Server.Init();
API.MainServer.Moderator.Init();
// Log info
API.Utility.Logging.MetaLog($"{Name} init complete.");
}


+ 60
- 0
CLre_server/Tweaks/Chat/Attributes.cs View File

@@ -0,0 +1,60 @@
using System.Reflection;
using System.Text.RegularExpressions;


namespace CLre_server.Tweaks.Chat
{
public class Attributes
{
}
[System.AttributeUsage(System.AttributeTargets.Method)]
public class ChatCommandAttribute : System.Attribute
{
public readonly string Name;
private readonly Regex _pattern;
private readonly Regex _usernamePattern;

public ChatCommandAttribute(string name, string pattern,
string usernamePattern = null,
RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase)
{
this.Name = name;
this._pattern = new Regex(pattern, options);
this._usernamePattern = usernamePattern == null ? null : new Regex(pattern, options);
Assembly asm = Assembly.GetCallingAssembly();
if (!ChatConnectionEngine._assembliesToCheck.Contains(asm))
{
ChatConnectionEngine._assembliesToCheck.Add(asm);
}

if (ChatHandler.IsAuthenticationReady && CLre.Config.chat_commands)
{
// Chat system is already started
// TODO
}
}

public Regex GetPattern()
{
return _pattern;
}

public Match RegexMatch(string sender, string message)
{
if (this._usernamePattern != null)
{
if (this._usernamePattern.IsMatch(sender))
{
return _pattern.Match(message);
}
}
else
{
return _pattern.Match(message);
}
return System.Text.RegularExpressions.Match.Empty;
}
}
}

+ 21
- 0
CLre_server/Tweaks/Chat/AuthenticationEngine.cs View File

@@ -0,0 +1,21 @@
namespace CLre_server.Tweaks.Chat
{
public class AuthenticationEngine: API.Engines.ServerEnginePostBuild
{
public API.Utility.AuthenticationResponse response = default;
public bool IsAuthenticated = false;
public override void Ready()
{
API.Utility.CardLifeUserAuthentication.Authenticate(
CLre.Config.email_address,
CLre.Config.password,
(data) =>
{
this.IsAuthenticated = true;
this.response = data;
API.Utility.Logging.Log("CLre chat credentials successfully authenticated");
}).Run();
}
}
}

+ 12
- 0
CLre_server/Tweaks/Chat/ChatConfig.cs View File

@@ -0,0 +1,12 @@
using System;

namespace CLre_server.Tweaks.Chat
{
[Serializable]
public struct ChatConfig
{
public bool commands_enabled;
public string email_address;
public string password;
}
}

+ 88
- 0
CLre_server/Tweaks/Chat/ChatConnectionEngine.cs View File

@@ -0,0 +1,88 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using ExitGames.Client.Photon;
using ExitGames.Client.Photon.Chat;
using HarmonyLib;
using Svelto.Context;

namespace CLre_server.Tweaks.Chat
{
public class ChatConnectionEngine: API.Engines.ServerEnginePostBuild, IWaitForFrameworkInitialization, IWaitForFrameworkDestruction
{
private bool _running = false;
private ChatClient _chatClient;
private ChatListener _chatListener;

public delegate void CommandHandler(Match messageMatch, ChatClient connection, string username);

private Dictionary<ChatCommandAttribute, CommandHandler> _handlers;
internal static List<Assembly> _assembliesToCheck = new List<Assembly>(new []{typeof(CLre).Assembly});
public override void Ready()
{
_running = true;
}

private IEnumerator connectify()
{
LoadHandlers(); // find & load commands
// wait for login to succeed (it may never)
while (!ChatHandler.IsAuthenticationReady && _running)
{
yield return null;
}
// login with authenticated credentials
// shout-out to however made an identical AuthenticationValues struct in the global namespace
ExitGames.Client.Photon.Chat.AuthenticationValues auth = new ExitGames.Client.Photon.Chat.AuthenticationValues();
auth.AuthType = ExitGames.Client.Photon.Chat.CustomAuthenticationType.Custom;
auth.AddAuthParameter("publicId", ChatHandler.PublicId);
auth.AddAuthParameter("token", ChatHandler.Token);
auth.UserId = ChatHandler.PublicId;
_chatListener= new ChatListener(_handlers);
_chatClient = new ChatClient(_chatListener, ConnectionProtocol.Udp);
_chatListener._chatClient = _chatClient;
_chatClient.Connect(Game.Utilities.CardLifePhotonSettings.PhotonChatAppID, "1.0", auth);
// run forever (until server shutsdown)
while (_running)
{
_chatClient.Service();
yield return null;
}
}

public void OnFrameworkInitialized()
{
connectify().Run();
}

public void OnFrameworkDestroyed()
{
_running = false;
}
private void LoadHandlers()
{
_handlers = new Dictionary<ChatCommandAttribute, CommandHandler>();
foreach (Assembly asm in _assembliesToCheck.ToArray())
{
foreach (Type t in asm.GetTypes())
{
foreach (MethodInfo m in t.GetMethods(AccessTools.all))
{
ChatCommandAttribute attr = m.GetCustomAttribute<ChatCommandAttribute>();
if (attr != null)
{
// TODO validate that method signature matches that of CommandHandler
API.Utility.Logging.MetaLog($"{t.FullName}:{m.Name} is handling {attr.Name}");
_handlers.Add(attr, (CommandHandler) Delegate.CreateDelegate(typeof(CommandHandler), m));
}
}
}
}
}
}
}

+ 33
- 0
CLre_server/Tweaks/Chat/ChatHandler.cs View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Reflection;

namespace CLre_server.Tweaks.Chat
{
public static class ChatHandler
{
private static AuthenticationEngine _chatAuthEngine = null;
private static ChatConnectionEngine _chatConnectionEngine = null;

internal static bool IsAuthenticationReady
{
get => _chatAuthEngine.IsAuthenticated;
}

internal static string PublicId
{
get => _chatAuthEngine.response.PublicId;
}

internal static string Token
{
get => _chatAuthEngine.response.Token;
}

public static void Init()
{
if (!CLre.Config.chat_commands) return;
_chatAuthEngine = new AuthenticationEngine();
_chatConnectionEngine = new ChatConnectionEngine();
}
}
}

+ 116
- 0
CLre_server/Tweaks/Chat/ChatListener.cs View File

@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using ExitGames.Client.Photon;
using ExitGames.Client.Photon.Chat;

namespace CLre_server.Tweaks.Chat
{
public class ChatListener: IChatClientListener
{
internal ChatClient _chatClient;
public static string ChatName;
private Dictionary<ChatCommandAttribute, ChatConnectionEngine.CommandHandler> _handlers;

public ChatListener(Dictionary<ChatCommandAttribute, ChatConnectionEngine.CommandHandler> handlers)
{
_handlers = handlers;
}
public void DebugReturn(DebugLevel level, string message)
{
API.Utility.Logging.Log($"ServerChatLog<{level}>: {message}");
}

public void OnDisconnected()
{
API.Utility.Logging.MetaLog("Chat disconnected");
}

public void OnConnected()
{
API.Utility.Logging.MetaLog("Chat connected");
// autoconnect to server's chat room
Room room = PhotonNetwork.room;
ChatName = "";
if (room != null)
{
ChatName = $"{room.Name}_chat_";
}
else
{
return;
}

bool result = _chatClient.Subscribe(new[] {ChatName}, 10);
API.Utility.Logging.MetaLog($"Subscribed to chat: {result}");
}

public void OnChatStateChange(ChatState state)
{
API.Utility.Logging.MetaLog($"Chat state changed to {state}");
}

public void OnGetMessages(string channelName, string[] senders, object[] messages)
{
if (channelName != ChatName) return; // just in case the server somehow gets subscribed to the wrong chat
for (int i = 0; i < senders.Length; i++)
{
string message = messages[i].ToString();
#if DEBUG
API.Utility.Logging.MetaLog($"Chat: `{senders[i]}` said `{messages[i]}` in `{channelName}`");
#endif
if (messages[i].ToString().ToLower() == "hello server")
{
_chatClient.PublishMessage(channelName, $"Hi {senders[i]}!");
}
else if (messages[i].ToString().ToLower() == "hello world")
{
_chatClient.PublishMessage(channelName, $"Hello fellow programmer {senders[i]}!");
}

if (message.StartsWith("/"))
{
string command = message.Substring(1);
string username = stripUsernameTag(senders[i]);
foreach (ChatCommandAttribute cca in _handlers.Keys)
{
var match = cca.RegexMatch(senders[i], command);
if (match.Success)
{
_handlers[cca](match, _chatClient, username);
}
}
}
}
}

public void OnPrivateMessage(string sender, object message, string channelName)
{
#if DEBUG
API.Utility.Logging.MetaLog($"Chat (private): `{sender}` said `{message}` in `{channelName}`");
#endif
}

public void OnSubscribed(string[] channels, bool[] results)
{
API.Utility.Logging.MetaLog($"Subscribed");
}

public void OnUnsubscribed(string[] channels)
{
API.Utility.Logging.MetaLog($"Unsubscribed");
}

public void OnStatusUpdate(string user, int status, bool gotMessage, object message)
{
API.Utility.Logging.MetaLog($"Status update: {user}->{status} ({gotMessage}:{message})");
}

private string stripUsernameTag(string sender)
{
if (!sender.Contains("]")) return sender;
return sender.Split(']')[1].Trim();
}
}
}

+ 111
- 0
CLre_server/Tweaks/Chat/ModeratorCommands.cs View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using ExitGames.Client.Photon.Chat;

namespace CLre_server.Tweaks.Chat
{
public static class ModeratorCommands
{
[ChatCommand("KICK", "kick ([\\w\\d\\-_]+)")]
public static void KickInButt(Match messageMatch, ChatClient connection, string sender)
{
if (!API.MainServer.Moderator.Instance.IsModerator(sender))
{
connection.PublishMessage(ChatListener.ChatName, "Ban failure: You're not a mod :(");
return;
}
string target = messageMatch.Groups[1].Value;
#if DEBUG
API.Utility.Logging.MetaLog($"/kick {target}");
#endif
if (API.MainServer.Moderator.Instance.DisconnectPlayerById(target))
{
connection.PublishMessage(ChatListener.ChatName, $"Adios {target}");
}
else if (API.MainServer.Moderator.Instance.DisconnectPlayerByName(target))
{
connection.PublishMessage(ChatListener.ChatName, $"Bye bye {target}");
}
else
{
connection.PublishMessage(ChatListener.ChatName, "Kick failure: User not found :(");
}
}
[ChatCommand("BAN", "ban ([\\w\\d\\-_]+)")]
public static void BanishIdiot(Match messageMatch, ChatClient connection, string sender)
{
if (!API.MainServer.Moderator.Instance.IsModerator(sender))
{
connection.PublishMessage(ChatListener.ChatName, "Ban failure: You're not a mod :(");
return;
}
string target = messageMatch.Groups[1].Value;
#if DEBUG
API.Utility.Logging.MetaLog($"/ban {target}");
#endif
if (API.MainServer.Moderator.Instance.BanPlayerById(target))
{
connection.PublishMessage(ChatListener.ChatName, $"And {target} is no more!");
}
else if (API.MainServer.Moderator.Instance.BanPlayerByName(target))
{
connection.PublishMessage(ChatListener.ChatName, $"And {target} was never seen again...");
}
else
{
connection.PublishMessage(ChatListener.ChatName, "Ban failure: User not found :(");
}
}

[ChatCommand("(SH)OPIFY", "(mod|op) ([\\w\\d\\-_]+)")]
public static void GoPro(Match messageMatch, ChatClient connection, string sender)
{
if (!API.MainServer.Moderator.Instance.IsModerator(sender))
{
connection.PublishMessage(ChatListener.ChatName, "Op failure: You're not a mod :(");
return;
}
string target = messageMatch.Groups[2].Value;
#if DEBUG
API.Utility.Logging.MetaLog($"/op {target}");
#endif
List<string> moderators = new List<string>(CLre.Config.moderators);
moderators.Add(target);
CLre.Config.moderators = moderators.ToArray();
connection.PublishMessage(ChatListener.ChatName, $"Promoted {target} to moderator");
}
[ChatCommand("De(SH)OPIFY", "(demod|deop) ([\\w\\d\\-_]+)")]
public static void AntiProton(Match messageMatch, ChatClient connection, string sender)
{
if (!API.MainServer.Moderator.Instance.IsModerator(sender))
{
connection.PublishMessage(ChatListener.ChatName, "DeOp failure: You're not a mod :(");
return;
}
string target = messageMatch.Groups[2].Value;
#if DEBUG
API.Utility.Logging.MetaLog($"/deop {target}");
#endif
List<string> moderators = new List<string>(CLre.Config.moderators);
if (moderators.Remove(target))
{
CLre.Config.moderators = moderators.ToArray();
connection.PublishMessage(ChatListener.ChatName, $"Demoted {target} to regular user");
}
connection.PublishMessage(ChatListener.ChatName, "DeOp failure: User not found :(");
}

[ChatCommand("ECHO", "echo (.+)$")]
public static void EchoEchoEcho(Match messageMatch, ChatClient connection, string sender)
{
string target = messageMatch.Groups[1].Value;
#if DEBUG
API.Utility.Logging.MetaLog($"/echo {target}");
#endif
connection.PublishMessage(ChatListener.ChatName, target);
}
}
}

+ 2
- 1
CLre_server/WebStatus/Attributes.cs View File

@@ -23,7 +23,8 @@ namespace CLre_server.WebStatus

if (WebServer.MainInstance != null && WebServer.MainInstance.IsRunning)
{
// Web server is already running
// TODO
}
}



+ 1
- 1
CLre_server/WebStatus/WebServer.cs View File

@@ -40,7 +40,7 @@ namespace CLre_server.WebStatus
{
if (!HttpListener.IsSupported)
{
API.Utility.Logging.LogWarning("HTTP Server is unsupported on earlier Windows versions. It will fail to start.");
API.Utility.Logging.LogWarning("HTTP Server is unsupported on earlier Windows versions. CLre webserver will fail to start.");
}
_httpListener = new HttpListener();
_httpListener.Prefixes.Add($"http://{ip}:{port}/");


Loading…
Cancel
Save