diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index ca058f39b..664371851 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -123,6 +123,7 @@ + diff --git a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs index 461837142..8d7fdfd36 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs @@ -703,7 +703,7 @@ namespace Barotrauma revokedCommands.Add(matchingCommand); } } - } + } client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Except(revokedCommands).ToList()); GameMain.Server.UpdateClientPermissions(client); @@ -869,9 +869,9 @@ namespace Barotrauma { if (GameMain.Server?.KarmaManager == null) { return; } GameMain.Server.KarmaManager.TestMode = !GameMain.Server.KarmaManager.TestMode; - NewMessage(GameMain.Server.KarmaManager.TestMode ? + NewMessage(GameMain.Server.KarmaManager.TestMode ? $"Karma test mode enabled by {client.Name}." : - $"Karma test mode disabled by {client.Name}.", + $"Karma test mode disabled by {client.Name}.", Color.LightGreen); GameMain.Server.SendDirectChatMessage( GameMain.Server.KarmaManager.TestMode ? "Karma test mode enabled." : "Karma test mode disabled.", @@ -1175,7 +1175,7 @@ namespace Barotrauma (Client client, Vector2 cursorPos, string[] args) => { string text = string.Join(" ", args); - text = client.Name+": " + text; + text = client.Name + ": " + text; if (GameMain.Server.OwnerConnection != null && client.Connection == GameMain.Server.OwnerConnection) { @@ -1212,6 +1212,18 @@ namespace Barotrauma GameMain.NetLobbyScreen.LevelSeed = string.Join(" ", args); })); + + commands.Add(new Command("lua", "lua: runs a string", (string[] args) => + { + GameMain.Lua.DoString(string.Join(" ", args)); + })); + + commands.Add(new Command("reloadlua", "reloads lua", (string[] args) => + { + GameMain.Lua = new LuaSetup(); + })); + + commands.Add(new Command("randomizeseed", "randomizeseed: Toggles level seed randomization on/off.", (string[] args) => { GameMain.Server.ServerSettings.RandomizeSeed = !GameMain.Server.ServerSettings.RandomizeSeed; diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs b/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs index 5cf61396e..80515d9bb 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Reflection; using System.Threading; using System.Xml.Linq; +using MoonSharp.Interpreter; namespace Barotrauma { @@ -20,6 +21,7 @@ namespace Barotrauma public static World World; public static GameSettings Config; + public static LuaSetup Lua; public static GameServer Server; public static NetworkMember NetworkMember @@ -131,6 +133,8 @@ namespace Barotrauma NetLobbyScreen = new NetLobbyScreen(); CheckContentPackage(); + + Lua = new LuaSetup(); } @@ -357,6 +361,8 @@ namespace Barotrauma TaskPool.Update(); CoroutineManager.Update((float)Timing.Step, (float)Timing.Step); + GameMain.Lua.hook.Call("think", new DynValue[] { DynValue.NewNumber(elapsedTime) }); + Timing.Accumulator -= Timing.Step; } diff --git a/Barotrauma/BarotraumaServer/ServerSource/Lua/LuaCustomConverters.cs b/Barotrauma/BarotraumaServer/ServerSource/Lua/LuaCustomConverters.cs new file mode 100644 index 000000000..8db9b3806 --- /dev/null +++ b/Barotrauma/BarotraumaServer/ServerSource/Lua/LuaCustomConverters.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Text; +using MoonSharp.Interpreter; +using Microsoft.Xna.Framework; + +namespace Barotrauma +{ + + public static class LuaCustomConverters + { + + public static void RegisterAll() + { + + // Vector 2 + + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Table, typeof(Vector2), + dynVal => { + Table table = dynVal.Table; + float x = (float)((Double)table[1]); + float y = (float)((Double)table[2]); + return new Vector2(x, y); + } + ); + Script.GlobalOptions.CustomConverters.SetClrToScriptCustomConversion( + (script, vector) => { + DynValue x = DynValue.NewNumber((double)vector.X); + DynValue y = DynValue.NewNumber((double)vector.Y); + DynValue dynVal = DynValue.NewTable(script, new DynValue[] { x, y }); + return dynVal; + } + ); + + // Vector3 + + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Table, typeof(Vector3), + dynVal => { + Table table = dynVal.Table; + float x = (float)((Double)table[1]); + float y = (float)((Double)table[2]); + float z = (float)((Double)table[3]); + return new Vector3(x, y, z); + } + ); + Script.GlobalOptions.CustomConverters.SetClrToScriptCustomConversion( + (script, vector) => { + DynValue x = DynValue.NewNumber((double)vector.X); + DynValue y = DynValue.NewNumber((double)vector.Y); + DynValue z = DynValue.NewNumber((double)vector.Z); + DynValue dynVal = DynValue.NewTable(script, new DynValue[] { x, y, z }); + return dynVal; + } + ); + + } + + } +} diff --git a/Barotrauma/BarotraumaServer/ServerSource/Lua/LuaScriptLoader.cs b/Barotrauma/BarotraumaServer/ServerSource/Lua/LuaScriptLoader.cs new file mode 100644 index 000000000..461ad7777 --- /dev/null +++ b/Barotrauma/BarotraumaServer/ServerSource/Lua/LuaScriptLoader.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using MoonSharp.Interpreter; +using MoonSharp.Interpreter.Loaders; + +namespace Barotrauma +{ + public class LuaScriptLoader : ScriptLoaderBase + { + public override object LoadFile(string file, Table globalContext) + { + return File.ReadAllText(file); + } + + public override bool ScriptFileExists(string file) + { + return File.Exists(file); + } + + public void RunFolder(string folder, Script script) + { + foreach(var str in DirSearch(folder)) + { + script.DoFile(str.Replace("\\", "/")); // i hate windows + } + } + + static string[] DirSearch(string sDir) + { + List files = new List(); + + try + { + foreach (string f in Directory.GetFiles(sDir)) + { + files.Add(f); + } + + foreach (string d in Directory.GetDirectories(sDir)) + { + foreach (string f in Directory.GetFiles(d)) + { + files.Add(f); + } + DirSearch(d); + } + } + catch (System.Exception excpt) + { + Console.WriteLine(excpt.Message); + } + + return files.ToArray(); + } + + + + + } +} diff --git a/Barotrauma/BarotraumaServer/ServerSource/Lua/LuaSetup.cs b/Barotrauma/BarotraumaServer/ServerSource/Lua/LuaSetup.cs new file mode 100644 index 000000000..420dbeacf --- /dev/null +++ b/Barotrauma/BarotraumaServer/ServerSource/Lua/LuaSetup.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using Barotrauma.Networking; +using MoonSharp.Interpreter; +using Microsoft.Xna.Framework; + +namespace Barotrauma +{ + class LuaSetup + { + + public Script lua; + public Hook hook; + + + public void DoString(string code) + { + try + { + lua.DoString(code); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + } + + private class Player + { + + public static List GetAllCharacters() + { + List values = new List(); + + foreach (Character ch in Character.CharacterList) + { + values.Add(UserData.Create(ch)); + } + + return values; + } + + public static List GetAllClients() + { + List values = new List(); + + foreach (Client ch in GameMain.Server.ConnectedClients) + { + values.Add(UserData.Create(ch)); + } + + return values; + } + + public static void SetClientCharacter(Client client, Character character) + { + GameMain.Server.SetClientCharacter(client, character); + } + + public static void Kick(Client client, string reason="") + { + GameMain.Server.KickClient(client.Connection, reason); + } + + public static void Ban(Client client, string reason = "", bool range = false, float seconds=-1) + { + if(seconds == -1) + { + GameMain.Server.BanClient(client, reason, range, null); + } + else + { + GameMain.Server.BanClient(client, reason, range, TimeSpan.FromSeconds(seconds)); + } + + } + + public static void StartGame() + { + GameMain.Server.StartGame(); + } + } + + private class Game + { + public static void SendMessage(string msg, int messageType = 0, Client sender = null, Character character = null) + { + GameMain.Server.SendChatMessage(msg, (ChatMessageType)messageType, sender, character); + } + + public static void Explode(Vector2 pos, float range=100, float force=30, float damage=30, float structureDamage=30, float itemDamage=30, float empStrength=0, float ballastFloraStrength=0) + { + new Explosion(range, force, damage, structureDamage, itemDamage, empStrength, ballastFloraStrength).Explode(pos, null); + } + + public static string Spawn(string name, Vector2 pos) + { + string error; + DebugConsole.SpawnCharacter(new string[] {name, "cursor"}, pos, out error); + return error; + } + + public static string SpawnItem(string name, Vector2 pos, bool inventory = false, Character character=null) + { + string error; + DebugConsole.SpawnItem(new string[] { name, inventory ? "inventory" : "cursor" }, pos, character, out error); + return error; + } + } + + + // hooks: + // chatMessage + // think + // update + // clientConnected + // clientDisconnected + // roundStart + // roundEnd + + public class Hook + { + public Script env; + + public Hook(Script e) + { + env = e; + } + + public class HookFunction + { + public string name; + public string hookName; + public DynValue function; + + public HookFunction(string n, string hn, DynValue func) + { + name = n; + hookName = hn; + function = func; + } + } + + public List hookFunctions = new List(); + + public void Add(string name, string hookName, DynValue function) + { + foreach (HookFunction hf in hookFunctions) + { + if(hf.hookName == hookName && hf.name == name) + { + hf.function = function; + + return; + } + } + + hookFunctions.Add(new HookFunction(name, hookName, function)); + } + + public void Call(string name, DynValue[] args) + { + foreach(HookFunction hf in hookFunctions) + { + if (hf.name == name) + { + try + { + env.Call(hf.function, args); + }catch(Exception e) + { + Console.WriteLine(e.ToString()); + } + } + } + } + } + + public LuaSetup() + { + + Console.WriteLine("Lua!"); + + LuaCustomConverters.RegisterAll(); + + LuaScriptLoader luaScriptLoader = new LuaScriptLoader(); + + //UserData.RegisterAssembly(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + + lua = new Script(CoreModules.Preset_SoftSandbox); + + lua.Options.ScriptLoader = luaScriptLoader; + + hook = new Hook(lua); + + lua.Globals["Player"] = new Player(); + lua.Globals["Game"] = new Game(); + lua.Globals["Hook"] = hook; + + + luaScriptLoader.RunFolder("Lua/autorun", lua); + + + } + + } + + + +} + + diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/ChatMessage.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/ChatMessage.cs index d6ea2bad9..39faa43e3 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/ChatMessage.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/ChatMessage.cs @@ -1,6 +1,7 @@ using Microsoft.Xna.Framework; using System; using System.Text; +using MoonSharp.Interpreter; namespace Barotrauma.Networking { @@ -169,6 +170,11 @@ namespace Barotrauma.Networking { GameMain.Server.SendChatMessage(txt, null, c); } + + + GameMain.Lua.hook.Call("chatMessage", new DynValue[] { DynValue.NewString(txt), UserData.Create(c) }); + + } public int EstimateLengthBytesServer(Client c) diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs index 3935c348b..37aa70cee 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs @@ -308,6 +308,9 @@ namespace Barotrauma.Networking SendConsoleMessage("Granted all permissions to " + newClient.Name + ".", newClient); } + GameMain.Lua.hook.Call("clientConnected", new MoonSharp.Interpreter.DynValue[] { MoonSharp.Interpreter.UserData.Create(newClient) }); + + SendChatMessage($"ServerMessage.JoinedServer~[client]={clName}", ChatMessageType.Server, null, changeType: PlayerConnectionChangeType.Joined); serverSettings.ServerDetailsChanged = true; @@ -2346,6 +2349,9 @@ namespace Barotrauma.Networking Log("Round started.", ServerLog.MessageType.ServerMessage); + GameMain.Lua.hook.Call("roundStart", new MoonSharp.Interpreter.DynValue[] { }); + + gameStarted = true; initiatedStartGame = false; GameMain.ResetFrameTime(); @@ -2461,6 +2467,9 @@ namespace Barotrauma.Networking Log("Ending the round...", ServerLog.MessageType.ServerMessage); } + GameMain.Lua.hook.Call("roundEnd", new MoonSharp.Interpreter.DynValue[] { }); + + string endMessage = TextManager.FormatServerMessage("RoundSummaryRoundHasEnded"); var traitorResults = TraitorManager?.GetEndResults() ?? new List(); @@ -2713,6 +2722,8 @@ namespace Barotrauma.Networking { if (client == null) return; + GameMain.Lua.hook.Call("clientDisconnected", new MoonSharp.Interpreter.DynValue[] { MoonSharp.Interpreter.UserData.Create(client) }); + if (gameStarted && client.Character != null) { client.Character.ClientDisconnected = true; diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index 08cc5c17b..8cd1d75e5 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -79,6 +79,10 @@ + + + + diff --git a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs index c928da73d..a0a73da59 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs @@ -37,6 +37,7 @@ namespace Barotrauma static partial class DebugConsole { + public partial class Command { public readonly string[] names; @@ -1689,7 +1690,7 @@ namespace Barotrauma return null; } - private static void SpawnCharacter(string[] args, Vector2 cursorWorldPos, out string errorMsg) + public static void SpawnCharacter(string[] args, Vector2 cursorWorldPos, out string errorMsg) { errorMsg = ""; if (args.Length == 0) { return; } @@ -1782,7 +1783,7 @@ namespace Barotrauma } } - private static void SpawnItem(string[] args, Vector2 cursorPos, Character controlledCharacter, out string errorMsg) + public static void SpawnItem(string[] args, Vector2 cursorPos, Character controlledCharacter, out string errorMsg) { errorMsg = ""; if (args.Length < 1) return; diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/GameMode.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/GameMode.cs index 6faaf1b13..dfc8516ab 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/GameMode.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/GameMode.cs @@ -1,76 +1,83 @@ using System; using System.Collections.Generic; +#if SERVER +using MoonSharp.Interpreter; +#endif namespace Barotrauma { - partial class GameMode - { - public static List PresetList = new List(); + partial class GameMode + { + public static List PresetList = new List(); - protected DateTime startTime; - - protected GameModePreset preset; - - public CrewManager CrewManager - { - get { return GameMain.GameSession?.CrewManager; } - } + protected DateTime startTime; - public virtual Mission Mission - { - get { return null; } - } + protected GameModePreset preset; - public bool IsSinglePlayer - { - get { return preset.IsSinglePlayer; } - } + public CrewManager CrewManager + { + get { return GameMain.GameSession?.CrewManager; } + } - public string Name - { - get { return preset.Name; } - } + public virtual Mission Mission + { + get { return null; } + } - public virtual bool Paused - { - get { return false; } - } + public bool IsSinglePlayer + { + get { return preset.IsSinglePlayer; } + } - public virtual void UpdateWhilePaused(float deltaTime) { } + public string Name + { + get { return preset.Name; } + } - public GameModePreset Preset - { - get { return preset; } - } + public virtual bool Paused + { + get { return false; } + } - public GameMode(GameModePreset preset) - { - this.preset = preset; - } + public virtual void UpdateWhilePaused(float deltaTime) { } - public virtual void Start() - { - startTime = DateTime.Now; - } + public GameModePreset Preset + { + get { return preset; } + } - public virtual void ShowStartMessage() { } - - public virtual void AddToGUIUpdateList() - { + public GameMode(GameModePreset preset) + { + this.preset = preset; + } + + public virtual void Start() + { + startTime = DateTime.Now; + } + + public virtual void ShowStartMessage() { } + + public virtual void AddToGUIUpdateList() + { #if CLIENT GameMain.GameSession?.CrewManager.AddToGUIUpdateList(); #endif - } + } - public virtual void Update(float deltaTime) - { - CrewManager?.Update(deltaTime); - } + public virtual void Update(float deltaTime) + { + CrewManager?.Update(deltaTime); - public virtual void End(CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None) - { - } +#if SERVER + GameMain.Lua.hook.Call("update", new DynValue[] { DynValue.NewNumber(deltaTime) }); +#endif + } - public virtual void Remove() { } - } + public virtual void End(CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None) + { + } + + public virtual void Remove() { } + } }