Track LocalMods as part of monolith

This commit is contained in:
2026-06-08 18:50:16 +03:00
parent 143f2fed7c
commit 1b214b44c2
1287 changed files with 139255 additions and 1 deletions
@@ -0,0 +1,112 @@
using Barotrauma.MoreLevelContent.Shared.Utils;
using MoreLevelContent.Shared;
using Barotrauma.MoreLevelContent.Shared.Config;
using MoreLevelContent;
using Barotrauma.Networking;
using MoreLevelContent.Networking;
using System.Collections.Generic;
using System.Linq;
using System;
// ISSUE WITH LEVEL GEN, IT'S THE RUIN MOVE
namespace Barotrauma.MoreLevelContent.Config
{
/// <summary>
/// Server
/// </summary>
partial class ConfigManager : Singleton<ConfigManager>
{
private void SetupServer()
{
NetUtil.Register(NetEvent.CONFIG_WRITE_SERVER, ServerRead);
NetUtil.Register(NetEvent.CONFIG_REQUEST, ConfigRequest);
// Always init the server with a default config, the first client to join with admin perms will set the config
// This was due to some issue with reading the config file on the server iirc
// Maybe revist this in the future?
Log.Debug("Setting up server config...");
// Only setup default config on non-dedicated servers, on servers hosted through
// the in game menu, the owner of the server will send the config to use
// where as on dedicated servers they will never get a config sent and thus will
// always have the default config loaded
if (!Main.IsDedicatedServer)
{
DefaultConfig();
} else
{
LoadConfig();
}
}
private readonly List<AccountId> correctInstalls = new List<AccountId>();
#region Networking
private void ServerRead(object[] args)
{
try
{
IReadMessage inMsg = (IReadMessage)args[0];
Client c = (Client)args[1];
Log.Debug($"Got config from client {c.Name}");
if (!c.HasPermission(ClientPermissions.ManageSettings))
{
Log.Error("No Perms!");
return;
}
if (!CheckClientVersion(c, inMsg.ReadString()))
{
Log.Debug($"Ignored config from {c.Name} due to them using the wrong version!");
return;
}
ReadNetConfig(ref inMsg);
ServerWrite();
} catch(Exception err)
{
Log.Debug(err.ToString());
}
}
private void ServerWrite()
{
Log.Debug("Propagating config to all clients...");
IWriteMessage outMsg = NetUtil.CreateNetMsg(NetEvent.CONFIG_WRITE_CLIENT);
WriteConfig(ref outMsg);
NetUtil.SendAll(outMsg);
}
private void ConfigRequest(object[] args)
{
IReadMessage inMsg = (IReadMessage)args[0];
Client c = (Client)args[1];
string version = inMsg.ReadString();
if (!CheckClientVersion(c, version)) return; // Exit if the client doesn't have the right version
IWriteMessage outMsg = NetUtil.CreateNetMsg(NetEvent.CONFIG_WRITE_CLIENT);
WriteConfig(ref outMsg);
NetUtil.SendClient(outMsg, c.Connection);
Log.Debug($"Sent config to client {c.Name}");
}
#endregion
private bool CheckClientVersion(Client client, string clientVersion)
{
if (!client.AccountId.TryUnwrap(out AccountId account)) return false;
if (correctInstalls.Contains(account)) return true;
if (clientVersion != Main.Version)
{
GameMain.Server.SendDirectChatMessage(
TextManager.GetServerMessage($"mlc.server.wrongversionclient~[clientversion]={clientVersion}~[serverversion]={Main.Version}").Value,
client,
ChatMessageType.ServerMessageBox);
GameMain.Server.SendChatMessage(TextManager.GetServerMessage($"mlc.server.wrongversion~[client]={client.Name}~[clientversion]={clientVersion}~[serverversion]={Main.Version}").Value);
return false;
}
GameMain.Server.SendChatMessage(TextManager.GetServerMessage($"mlc.server.installed~[client]={client.Name}").Value);
correctInstalls.Add(account);
return true;
}
}
}
@@ -0,0 +1,81 @@
using Barotrauma;
using Barotrauma.MoreLevelContent.Shared.Utils;
using Barotrauma.Networking;
using MoreLevelContent.Networking;
using MoreLevelContent.Shared.Data;
using MoreLevelContent.Shared.Generation.Pirate;
using System;
using System.Linq;
namespace MoreLevelContent.Shared.Generation
{
// Server
public partial class MapDirector : Singleton<MapDirector>
{
partial void SetupProjSpecific()
{
NetUtil.Register(NetEvent.MAP_CONNECTION_EQUALITYCHECK_REQUEST, RequestConnectionEquality);
NetUtil.Register(NetEvent.MAP_REQUEST_STATE, RespondToMapStateRequest);
}
private void RespondToMapStateRequest(object[] args)
{
Client client = (Client)args[1];
IWriteMessage outMsg = NetUtil.CreateNetMsg(NetEvent.MAP_SEND_STATE);
WriteMsg();
NetUtil.SendClient(outMsg, client.Connection);
Log.Debug($"Sent map state request to {client.Name}");
void WriteMsg()
{
if (GameMain.GameSession.Campaign is not MultiPlayerCampaign campaign)
{
outMsg.WriteByte((byte)MapSyncState.NotCampaign);
return;
}
if (campaign.Map == null)
{
outMsg.WriteByte((byte)MapSyncState.MapNotCreated);
return;
}
outMsg.WriteByte((byte)MapSyncState.MapSynced);
var activeDistressBeacons = campaign.Map.Connections.Where(c => c.LevelData.MLC().HasDistress);
var count = activeDistressBeacons.Count();
if (count > byte.MaxValue)
{
DebugConsole.ThrowError("More Level Content detected more than 255 active distress beacons when trying to respond to a client map state request, what did you do??? This won't work, please reduce the numer!");
return;
}
outMsg.WriteByte((byte)count);
foreach (var connection in activeDistressBeacons)
{
int id = ConnectionIdLookup[connection];
outMsg.WriteInt16((short)id);
outMsg.WriteByte((byte)connection.LevelData.MLC().DistressStepsLeft);
}
}
}
internal void NotifyMapFeatureRevealed(LocationConnection con, MapFeatureData feature)
{
foreach (Client client in GameMain.Server.ConnectedClients)
{
NotifyMapFeatureRevealed(client, con, feature);
}
}
internal partial void RoundEnd(CampaignMode.TransitionType transitionType) { }
private void NotifyMapFeatureRevealed(Client client, LocationConnection con, MapFeatureData feature)
{
Int32 conId = MapDirector.ConnectionIdLookup[con];
var msg = NetUtil.CreateNetMsg(NetEvent.EVENT_REVEALMAPFEATURE);
msg.WriteIdentifier(feature.Name);
msg.WriteInt32(conId);
NetUtil.SendClient(msg, client.Connection);
}
}
}
@@ -0,0 +1,30 @@
using Barotrauma.Networking;
using Barotrauma;
using MoreLevelContent.Networking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MoreLevelContent.Shared.Data;
using Barotrauma.MoreLevelContent.Config;
namespace MoreLevelContent.Shared.Generation
{
// Server
internal partial class OldDistressMapModule
{
protected override void InitProjSpecific() => NetUtil.Register(NetEvent.COMMAND_CREATEDISTRESS, Command_CreateDistress);
internal void Command_CreateDistress(object[] args)
{
Log.Debug("Got command");
ForceDistress();
}
}
internal partial class DistressMapModule : TimedEventMapModule
{
protected override void InitProjSpecific() { }
}
}
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MoreLevelContent.Shared.Generation
{
// Server
internal partial class LostCargoMapModule : TimedEventMapModule
{
protected override void InitProjSpecific() { }
}
}
@@ -0,0 +1,84 @@
using Barotrauma;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using MoreLevelContent.Shared;
using MoreLevelContent.Shared.Utils;
using System;
using System.Linq;
using System.Xml.Linq;
namespace MoreLevelContent.Items
{
// Server
internal partial class SimpleStore : Powered, IServerSerializable, IClientSerializable
{
public void ServerEventRead(IReadMessage msg, Client c)
{
uint recipeHash = msg.ReadUInt32();
int amountToFabricate = msg.ReadRangedInteger(1, MaxAmountToFabricate);
item.CreateServerEvent(this);
if (!item.CanClientAccess(c)) { return; }
AmountToFabricate = amountToFabricate;
if (recipeHash == 0)
{
CancelFabricating(c.Character);
}
else
{
//if already fabricating the selected item, return
if (fabricatedItem != null && fabricatedItem.RecipeHash == recipeHash) { return; }
if (recipeHash == 0) { return; }
amountRemaining = AmountToFabricate;
StartFabricating(fabricationRecipes[recipeHash], c.Character);
}
}
private ulong serverEventId = 0;
private readonly struct EventData : IEventData
{
public readonly ulong ServerEventId;
public readonly SimpleStoreState State;
public EventData(ulong serverEventId, SimpleStoreState state)
{
//ensuring the uniqueness of this event is
//required for the fabricator to sync correctly;
//otherwise, the event manager would incorrectly
//assume that the client actually has the latest state
ServerEventId = serverEventId;
State = state;
}
}
public override IEventData ServerGetEventData()
=> new EventData(serverEventId, State);
public override bool ValidateEventData(NetEntityEvent.IData data)
=> TryExtractEventData<EventData>(data, out _);
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
var componentData = ExtractEventData<EventData>(extraData);
msg.WriteByte((byte)componentData.State);
msg.WriteRangedInteger(AmountToFabricate, 0, MaxAmountToFabricate);
msg.WriteRangedInteger(amountRemaining, 0, MaxAmountToFabricate);
msg.WriteSingle(timeUntilReady);
uint recipeHash = fabricatedItem?.RecipeHash ?? 0;
msg.WriteUInt32(recipeHash);
UInt16 userId = fabricatedItem is null || user is null ? (UInt16)0 : user.ID;
msg.WriteUInt16(userId);
msg.WriteUInt16((ushort)fabricationLimits.Count);
foreach (var kvp in fabricationLimits)
{
msg.WriteUInt32(kvp.Key);
msg.WriteUInt32((uint)kvp.Value);
}
}
}
}
+73
View File
@@ -0,0 +1,73 @@
using Barotrauma;
using Barotrauma.Networking;
using HarmonyLib;
using MoreLevelContent.Networking;
using MoreLevelContent.Shared;
using MoreLevelContent.Shared.Generation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
namespace MoreLevelContent
{
// Server
partial class Main
{
public static bool IsDedicatedServer => GameMain.Server.OwnerConnection == null;
public static bool CurrentGameModeValid = true;
public void InitServer()
{
Log.Debug("Init Server");
Harmony.Patch(AccessTools.PropertySetter(typeof(NetLobbyScreen), nameof(NetLobbyScreen.SelectedModeIndex)), postfix: new HarmonyMethod(AccessTools.Method(typeof(Main), nameof(Main.OnGameModeChange))));
OnGameModeChange(GameMain.NetLobbyScreen);
if (PreventRoundEnd)
{
Main.Harmony.Patch(AccessTools.Method(typeof(GameServer), nameof(GameServer.Update)), transpiler: new HarmonyMethod(AccessTools.Method(typeof(Main), nameof(Main.PatchEndRound))));
}
}
static IEnumerable<CodeInstruction> PatchEndRound(IEnumerable<CodeInstruction> instructions, ILGenerator il)
{
Log.Debug(">>>> Starting end round transpile");
var code = new List<CodeInstruction>(instructions);
for (int i = 0; i < code.Count; i++)
{
if (i >= 514 && i <= 519)
{
Log.Debug($"nop {i}");
code[i].opcode = OpCodes.Nop;
}
yield return code[i];
}
}
static void OnGameModeChange(NetLobbyScreen __instance)
{
var gameMode = __instance.GameModes[__instance.SelectedModeIndex];
CurrentGameModeValid = gameMode.GameModeType == typeof(MultiPlayerCampaign);
var validClients = GameMain.Server.ConnectedClients.Where(c => c.HasPermission(ClientPermissions.SelectMode));
if (!CurrentGameModeValid)
{
foreach (var client in validClients)
{
GameMain.Server.SendDirectChatMessage(
TextManager.GetServerMessage($"mlc.gamemodewarning.description").Value,
client,
ChatMessageType.ServerMessageBox);
}
}
}
static void SetRoundEndDelay()
{
Log.Debug("called");
var endRoundDelay = AccessTools.PropertySetter(typeof(GameServer), nameof(GameServer.EndRoundDelay));
var endRoundTimer = AccessTools.PropertySetter(typeof(GameServer), nameof(GameServer.EndRoundTimer));
endRoundTimer.Invoke(GameMain.Server, new object[] { 0 });
endRoundDelay.Invoke(GameMain.Server, new object[] { 1000f });
Log.Debug($"{GameMain.Server.EndRoundDelay} {GameMain.Server.EndRoundTimer}");
}
}
}
@@ -0,0 +1,15 @@
using Barotrauma;
using Barotrauma.Networking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MoreLevelContent.Missions
{
// Server
internal partial class CablePuzzleMission : Mission
{
}
}
@@ -0,0 +1,21 @@
using Barotrauma;
using Barotrauma.Networking;
using MoreLevelContent.Shared.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MoreLevelContent.Missions
{
// Server
partial class DistressEscortMission : DistressMission
{
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
missionNPCs.Write(msg);
}
}
}
@@ -0,0 +1,19 @@
using Barotrauma.Networking;
using MoreLevelContent.Shared;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MoreLevelContent.Missions
{
partial class DistressGhostshipMission
{
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
missionNPCs.Write(msg);
}
}
}
@@ -0,0 +1,23 @@
using Barotrauma.Networking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MoreLevelContent.Missions
{
// Server
partial class DistressSubmarineMission : DistressMission
{
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
missionNPCs.Write(msg);
foreach (var character in rewardLookup.Keys)
{
msg.WriteUInt16((ushort)rewardLookup[character]);
}
}
}
}
@@ -0,0 +1,33 @@
using Barotrauma.Networking;
using Barotrauma;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MoreLevelContent.Shared.Data;
namespace MoreLevelContent.Missions
{
// Server
partial class MissionNPCCollection
{
internal void Write(IWriteMessage msg)
{
msg.WriteBoolean(characters.Count > 0);
if (characters.Count == 0) return;
msg.WriteByte((byte)characters.Count);
foreach (Character character in characters)
{
character.WriteSpawnData(msg, character.ID, restrictMessageSize: false);
msg.WriteBoolean(character.MLC().NPCElement.GetAttributeBool("allowordering", false));
msg.WriteUInt16((ushort)characterItems[character].Count());
foreach (Item item in characterItems[character])
{
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? Entity.NullEntityID, 0, item.ParentInventory?.FindIndex(item) ?? -1);
}
}
}
}
}
@@ -0,0 +1,9 @@
using Barotrauma;
namespace MoreLevelContent.Missions
{
// Server
internal partial class TriangulationMission : Mission
{
}
}
@@ -0,0 +1,34 @@
using Barotrauma;
using Barotrauma.Networking;
namespace MoreLevelContent.Networking
{
/// <summary>
/// Server
/// </summary>
public static partial class NetUtil
{
/// <summary>
/// Send a message to the specified client
/// </summary>
/// <param name="outMsg"></param>
/// <param name="connection"></param>
/// <param name="deliveryMethod"></param>
internal static void SendClient(IWriteMessage outMsg, NetworkConnection connection, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable)
{
if (GameMain.IsSingleplayer) return;
GameMain.LuaCs.Networking.Send(outMsg, connection, deliveryMethod);
}
/// <summary>
/// Send message to all connected clients
/// </summary>
/// <param name="outMsg"></param>
/// <param name="deliveryMethod"></param>
internal static void SendAll(IWriteMessage outMsg, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable)
{
if (GameMain.IsSingleplayer) return;
GameMain.LuaCs.Networking.Send(outMsg, null, deliveryMethod);
}
}
}