### Initialize Mod and SteamAPI - C# Source: https://context7.com/svenvth/steamgameservermod/llms.txt The core entry point for the SteamGameServerMod. It initializes the SteamAPI, loads server settings, and starts server-related coroutines. It supports both MelonLoader and BepInEx mod loaders and can be launched in host mode using the '--host' command-line argument. ```csharp // Core.cs - Main mod entry point // Supports both MelonLoader and BepInEx mod loaders namespace SteamGameServerMod { // MelonLoader: public class Core : MelonMod // BepInEx: public class Core : BaseUnityPlugin public static bool IsHost; // Initialization (MelonLoader: OnInitializeMelon, BepInEx: Awake) void Initialize() { var startArguments = Environment.GetCommandLineArgs().ToList(); IsHost = startArguments.Contains("--host"); if (IsHost) { if (!SteamAPI.Init()) { Log.LogFatal("Failed to initialize SteamAPI!"); return; } // Load settings and initialize server _settings = new SettingsManager().LoadSettings(); _gameServer = new GameServerManager(_settings); // Start initialization coroutines StartCoroutine(_gameServer.Initialize()); StartCoroutine(_gameServer.InitializeServerSpawning()); } } } // Launch the game with dedicated server mode: // "Schedule I.exe" --host ``` -------------------------------- ### Initialize Steam Game Server and Create Lobby (C#) Source: https://context7.com/svenvth/steamgameservermod/llms.txt Initializes the Steam Game Server, configures server settings such as ports, mode, and player count, and then creates a public lobby. It requires Steamworks.NET and basic server configuration settings. ```csharp // Managers/GameServerManager.cs using Steamworks; namespace SteamGameServerMod.Managers { internal class GameServerManager { private readonly GameServerSettings _settings; private bool _serverInitialized; public IEnumerator Initialize() { RegisterCallbacks(); if (!SteamAPI.IsSteamRunning()) { Log.LogError("Steam is not running."); yield break; } // Initialize Steam GameServer var success = GameServer.Init( 0u, // IP (0 = any) _settings.QueryPort, // Query port (default: 27016) _settings.GamePort, // Game port (default: 27015) _settings.ServerMode, // Server mode "1.0.0" // Version string ); if (!success) { Log.LogError("Failed to initialize Steam GameServer."); yield break; } // Configure server settings SteamGameServer.SetModDir(_settings.GameDescription); SteamGameServer.SetGameDescription(_settings.GameDescription); SteamGameServer.SetProduct($"{_settings.AppID}"); SteamGameServer.SetDedicatedServer(true); SteamGameServer.SetMaxPlayerCount(_settings.MaxPlayers); SteamGameServer.SetPasswordProtected(_settings.PasswordProtected); SteamGameServer.SetServerName(_settings.ServerName); SteamGameServer.SetMapName(_settings.MapName); // Log on to Steam SteamGameServer.LogOn(_settings.Token); yield return new WaitUntil(SteamGameServer.BLoggedOn); _serverInitialized = true; SteamMatchmaking.CreateLobby(ELobbyType.k_ELobbyTypePublic, _settings.MaxPlayers); } } } ``` -------------------------------- ### Handle Server Spawning and Networking Initialization (C#) Source: https://context7.com/svenvth/steamgameservermod/llms.txt Manages scene loading, player spawning, and save game loading for the server host. It integrates with FishNet for networking and requires access to SaveGameManager, LoadManager, and SceneManager. ```csharp // Managers/GameServerManager.cs public IEnumerator InitializeServerSpawning() { yield return new WaitUntil(() => _lobbyGameCreated); // Load save file var saveFile = SaveGameManager.GetSave(_settings.SaveGameName); LoadManager.Instance.ActiveSaveInfo = saveFile; LoadManager.Instance.IsLoading = true; // Load main game scene var sceneLoading = SceneManager.LoadSceneAsync("Main"); yield return new WaitUntil(() => sceneLoading.isDone); // Initialize FishNet networking var fishySteamworks = InstanceFinder.TransportManager .GetTransport() .GetTransport(); fishySteamworks.OnServerConnectionState += _ => { // Connect local client after server starts InstanceFinder.TransportManager.GetTransport() .SetClientTransport(); InstanceFinder.NetworkManager.ClientManager.StartConnection(); }; fishySteamworks.SetClientAddress("0.0.0.0"); fishySteamworks.StartConnection(server: true); yield return new WaitUntil(() => InstanceFinder.IsClient && InstanceFinder.IsServer); yield return new WaitUntil(() => Player.Local); // Load save data yield return LoadSave(LoadManager.Instance.ActiveSaveInfo); LoadManager.Instance.IsGameLoaded = true; } ``` -------------------------------- ### Unified Logging Utility in C# Source: https://context7.com/svenvth/steamgameservermod/llms.txt Provides a unified logging abstraction that supports both MelonLoader and BepInEx logging systems. This allows for consistent logging across different modding frameworks. It includes methods for logging informational messages, warnings, errors, and fatal errors. ```csharp // Logging/Log.cs namespace SteamGameServerMod.Logging { public static class Log { // BepInEx: ManualLogSource Logger // MelonLoader: Melon.Logger public static void LogInfo(string message) { // BepInEx: Logger.LogInfo(message); // MelonLoader: Melon.Logger.Msg(message); } public static void LogWarning(string message) { // BepInEx: Logger.LogWarning(message); // MelonLoader: Melon.Logger.Warning(message); } public static void LogError(string message) { // BepInEx: Logger.LogError(message); // MelonLoader: Melon.Logger.Error(message); } public static void LogFatal(string message) { // BepInEx: Logger.LogFatal(message); // MelonLoader: Melon.Logger.BigError(message); } } } // Usage examples: // Log.LogInfo("Steam GameServer Mod Initializing..."); // Log.LogWarning("Settings file not found, using defaults"); // Log.LogError("Failed to initialize Steam GameServer"); // Log.LogFatal("Critical failure - cannot continue"); ``` -------------------------------- ### SettingsManager for JSON Configuration - C# Source: https://context7.com/svenvth/steamgameservermod/llms.txt Manages the loading and saving of server configuration from a JSON file. It handles file existence checks and deserializes the JSON content into `GameServerSettings` objects. If the configuration file is not found, it creates a default settings file. ```csharp // Managers/SettingsManager.cs using Newtonsoft.Json; namespace SteamGameServerMod.Managers { internal class SettingsManager { const string SettingsFileName = "SteamGameServerSettings.json"; public GameServerSettings LoadSettings() { // MelonLoader: UserData/SteamGameServerSettings.json // BepInEx: BepInEx/config/SteamGameServerSettings.json var settingsPath = Path.Combine("BepInEx", "config", SettingsFileName); if (File.Exists(settingsPath)) { var json = File.ReadAllText(settingsPath); return JsonConvert.DeserializeObject(json); } // Create default settings if not found var defaultSettings = new GameServerSettings { AppID = 3164500 }; SaveSettings(defaultSettings); return defaultSettings; } void SaveSettings(GameServerSettings settings) { var json = JsonConvert.SerializeObject(settings, Formatting.Indented); File.WriteAllText(settingsPath, json); } } } // Usage: var settingsManager = new SettingsManager(); var settings = settingsManager.LoadSettings(); // settings.ServerName = "My Dedicated Server" // settings.MaxPlayers = 16 // settings.GamePort = 27015 ``` -------------------------------- ### Save Game Management in C# Source: https://context7.com/svenvth/steamgameservermod/llms.txt Manages the creation and loading of save games for multiplayer sessions. It handles file paths, reads metadata and game data, and can create new save files from a template. Dependencies include ScheduleOne.Persistence and its data classes. ```csharp // Managers/SaveGameManager.cs using ScheduleOne.Persistence; using ScheduleOne.Persistence.Datas; namespace SteamGameServerMod.Managers { public static class SaveGameManager { // Get or create a save file public static SaveInfo GetSave(string saveName) { var savePath = Path.Combine( SaveManager.Instance.IndividualSavesContainerPath, saveName ); if (!Directory.Exists(savePath)) CreateNewSave(saveName, Application.version); var metaData = ReadJsonData( Path.Combine(savePath, "MetaData.json") ); var gameData = ReadJsonData( Path.Combine(savePath, "Game.json") ); var moneyData = ReadJsonData( Path.Combine(savePath, "Money.json") ); return new SaveInfo( savePath, 0, gameData.OrganisationName, metaData.CreationDate.GetDateTime(), metaData.LastPlayedDate.GetDateTime(), moneyData.Networth, metaData.LastSaveVersion, metaData ); } // Create a new save with default data public static void CreateNewSave(string saveName, string gameVersion) { var savePath = Path.Combine( SaveManager.Instance.IndividualSavesContainerPath, saveName ); Directory.CreateDirectory(savePath); // Copy default save template CopyDirectory( Path.Combine(Application.streamingAssetsPath, "DefaultSave"), savePath, recursive: true ); // Write game data File.WriteAllText( Path.Combine(savePath, "Game.json"), new GameData { Seed = Random.Range(0, int.MaxValue), OrganisationName = saveName, Settings = new(), GameVersion = gameVersion }.GetJson() ); // Extract embedded player data using var stream = Assembly.GetExecutingAssembly() .GetManifestResourceStream("SteamGameServerMod.Assets.Player_0.zip"); new ZipArchive(stream).ExtractToDirectory( Path.Combine(savePath, "Players") ); } } } ``` -------------------------------- ### GameServerSettings Model - C# Source: https://context7.com/svenvth/steamgameservermod/llms.txt Defines the configuration structure for dedicated server settings, including server name, game version, player count, ports, and authentication details. This model is used for loading settings from a JSON file and is essential for configuring the server's behavior. ```csharp // Settings/GameServerSettings.cs using Steamworks; namespace SteamGameServerMod.Settings { public class GameServerSettings { public string ServerName { get; set; } = "[S1] OneM Testing"; public string GameDescription { get; set; } = "Schedule 1"; public string GameVersion { get; set; } = "0.3.4f8 Alternate"; public string MapName { get; set; } = "default_map"; public EServerMode ServerMode { get; set; } = EServerMode.eServerModeNoAuthentication; public int MaxPlayers { get; set; } = 16; public ushort GamePort { get; set; } = 27015; public ushort QueryPort { get; set; } = 27016; public bool PasswordProtected { get; set; } = false; public string ServerPassword { get; set; } = ""; public uint AppID { get; set; } public string Token { get; set; } = string.Empty; public string SaveGameName { get; set; } = "S1MP_Testing"; } } // Example SteamGameServerSettings.json configuration: { "ServerName": "My Dedicated Server", "GameDescription": "Schedule 1", "GameVersion": "0.3.4f8 Alternate", "MapName": "default_map", "ServerMode": 1, "MaxPlayers": 16, "GamePort": 27015, "QueryPort": 27016, "PasswordProtected": false, "ServerPassword": "", "AppID": 3164500, "Token": "", "SaveGameName": "MyServerSave" } ``` -------------------------------- ### Steam Utility Functions for IP Conversion and Error Codes (C#) Source: https://context7.com/svenvth/steamgameservermod/llms.txt Provides utility functions for network and Steam API interactions. It includes methods to convert IP address strings to a uint32 format required by some Steam API calls and to translate Steam's EDenyReason and EResult enums into human-readable strings for easier debugging and user feedback. ```csharp // Util/Utils.cs using System.Net; using Steamworks; namespace SteamGameServerMod.Util { internal class Utils { // Convert IP address string to uint32 for Steam API public static uint IpToUInt32(string ipAddress) { byte[] bytes = IPAddress.Parse(ipAddress).GetAddressBytes(); return ((uint)bytes[0]) | ((uint)bytes[1] << 8) | ((uint)bytes[2] << 16) | ((uint)bytes[3] << 24); } // Convert EDenyReason to human-readable string public static string GetDenyReasonText(EDenyReason reason) { return reason switch { EDenyReason.k_EDenyInvalid => "Invalid", EDenyReason.k_EDenyInvalidVersion => "Invalid Version", EDenyReason.k_EDenyNotLoggedOn => "Not Logged On", EDenyReason.k_EDenyNoLicense => "No License", EDenyReason.k_EDenyCheater => "Cheater", EDenyReason.k_EDenyLoggedInElseWhere => "Logged In Elsewhere", _ => $"Unknown ({{reason}})" }; } // Convert EResult to human-readable string public static string GetResultText(EResult result) { return result switch { EResult.k_EResultOK => "Success", EResult.k_EResultFail => "Generic failure", EResult.k_EResultNoConnection => "No connection", EResult.k_EResultTimeout => "Timeout", EResult.k_EResultBanned => "Banned", EResult.k_EResultAccessDenied => "Access denied", _ => $"Unknown ({{result}})" }; } } } // Usage: uint serverIp = Utils.IpToUInt32("192.168.1.100"); string errorText = Utils.GetDenyReasonText(EDenyReason.k_EDenyNoLicense); // Output: "No License" ``` -------------------------------- ### Intercept Steam Lobby Events with Harmony (C#) Source: https://context7.com/svenvth/steamgameservermod/llms.txt Applies Harmony patches to intercept and modify Steam lobby creation and join events. It handles scenarios like skipping default lobby creation, checking for lobby fullness, validating game versions, and ensuring the lobby is ready before allowing a client to join. It also overrides the IsHost property. ```csharp // Patches/LobbyPatches.cs using HarmonyLib; using ScheduleOne.Networking; using Steamworks; namespace SteamGameServerMod.Patches { [HarmonyPatch(typeof(Lobby))] public class LobbyPatches { // Skip default lobby creation when hosting [HarmonyPatch(nameof(Lobby.OnLobbyCreated))] [HarmonyPrefix] public static bool OnLobbyCreated_Prefix() => false; // Handle lobby join with version checking [HarmonyPatch(nameof(Lobby.OnLobbyEntered))] [HarmonyPrefix] public static bool OnLobbyEntered_Prefix(Lobby __instance, LobbyEnter_t result) { if (Core.IsHost) return false; var enterResponse = (EChatRoomEnterResponse)result.m_EChatRoomEnterResponse; if (enterResponse == EChatRoomEnterResponse.k_EChatRoomEnterResponseFull) { LeaveLobbyWithStatusMessage(__instance, "Lobby full", "The lobby you have tried to enter is full!", isBad: true); return false; } // Version compatibility check var lobbyVersion = SteamMatchmaking.GetLobbyData( (CSteamID)result.m_ulSteamIDLobby, "version"); if (lobbyVersion != Application.version) { LeaveLobbyWithStatusMessage(__instance, "Version Mismatch", $"Host version: {lobbyVersion}\nYour version: {Application.version}", isBad: true); return false; } // Check if lobby is ready var isReady = SteamMatchmaking.GetLobbyData( (CSteamID)result.m_ulSteamIDLobby, "ready") == "true"; if (!isReady) { LeaveLobbyWithStatusMessage(__instance, "Lobby not ready", "Lobby is not ready yet, try again later.", isBad: true); return false; } var lobbyOwnerSteamId = SteamMatchmaking.GetLobbyOwner( (CSteamID)result.m_ulSteamIDLobby).m_SteamID; LoadManager.Instance.LoadAsClient(lobbyOwnerSteamId.ToString()); return false; } // Override IsHost property [HarmonyPatch(nameof(Lobby.IsHost), MethodType.Getter)] [HarmonyPrefix] public static void IsHost_Prefix(ref bool __result) => __result = Core.IsHost; } } ``` -------------------------------- ### Process Steam Callbacks and Shutdown Server (C#) Source: https://context7.com/svenvth/steamgameservermod/llms.txt Handles the continuous processing of Steam callbacks during runtime and provides a method for gracefully shutting down the Steam Game Server. This includes logging off from Steam and releasing server resources. ```csharp // Managers/GameServerManager.cs public void Update() { if (_serverInitialized) { GameServer.RunCallbacks(); SteamAPI.RunCallbacks(); } } public void Shutdown() { if (_serverInitialized) { Log.LogInfo("Shutting down Steam GameServer..."); SteamGameServer.SetAdvertiseServerActive(false); SteamGameServer.LogOff(); GameServer.Shutdown(); _serverInitialized = false; } } // Callback registration for lobby events: void RegisterCallbacks() { Callback.Create(lobbyCreated => { SteamMatchmaking.SetLobbyGameServer( (CSteamID)lobbyCreated.m_ulSteamIDLobby, 0, _settings.GamePort, CSteamID.Nil ); SteamMatchmaking.SetLobbyData( (CSteamID)lobbyCreated.m_ulSteamIDLobby, "version", _settings.GameVersion ); SteamMatchmaking.SetLobbyData( (CSteamID)lobbyCreated.m_ulSteamIDLobby, "ready", "true" ); SteamGameServer.SetAdvertiseServerActive(true); }); Callback.Create(_ => { _lobbyGameCreated = true; }); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.