Server LuaCsForBarotrauma
GameMain.cs
2 using Barotrauma.Steam;
3 using FarseerPhysics.Dynamics;
4 using Microsoft.Xna.Framework;
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using Barotrauma.IO;
9 using System.Linq;
10 using System.Reflection;
11 using System.Threading;
12 using System.Xml.Linq;
13 using MoonSharp.Interpreter;
14 using System.Net;
16 
17 namespace Barotrauma
18 {
19  class GameMain
20  {
21  public static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
22 
23  public static bool IsSingleplayer => NetworkMember == null;
24  public static bool IsMultiplayer => NetworkMember != null;
25 
26  private static World world;
27  public static World World
28  {
29  get
30  {
31  if (world == null) { world = new World(new Vector2(0, -9.82f)); }
32  return world;
33  }
34  set { world = value; }
35  }
36 
37  public static LuaCsSetup LuaCs;
38 
39  public static GameServer Server;
41  {
42  get { return Server; }
43  }
44 
45  public static GameSession GameSession;
46 
47  public static GameMain Instance
48  {
49  get;
50  private set;
51  }
52 
53  public static Thread MainThread { get; private set; }
54 
55  //only screens the server implements
56  public static GameScreen GameScreen;
58 
59  //null screens because they are not implemented by the server,
60  //but they're checked for all over the place
61  //TODO: maybe clean up instead of having these constants
63 
64  public static bool ShouldRun = true;
65 
66  private static Stopwatch stopwatch;
67 
68  private static readonly Queue<int> prevUpdateRates = new Queue<int>();
69  private static int updateCount = 0;
70 
71  public static ContentPackage VanillaContent => ContentPackageManager.VanillaCorePackage;
72 
73 
74  public readonly string[] CommandLineArgs;
75 
76  public GameMain(string[] args)
77  {
78  Instance = this;
79 
80  CommandLineArgs = args;
81 
82  World = new World(new Vector2(0, -9.82f));
83  FarseerPhysics.Settings.AllowSleep = true;
84  FarseerPhysics.Settings.ContinuousPhysics = false;
85  FarseerPhysics.Settings.VelocityIterations = 1;
86  FarseerPhysics.Settings.PositionIterations = 1;
87 
88  Console.WriteLine("Loading game settings");
89  GameSettings.Init();
90 
91  //no owner key = dedicated server
92  if (!CommandLineArgs.Any(a => a.Trim().Equals("-ownerkey", StringComparison.OrdinalIgnoreCase)))
93  {
94  Console.WriteLine("Initializing SteamManager");
95  SteamManager.Initialize();
96 
97  if (!SteamManager.SteamworksLibExists)
98  {
99  Console.WriteLine("Initializing EosManager");
100  if (EosInterface.Core.Init(EosInterface.ApplicationCredentials.Server, enableOverlay: false).TryUnwrapFailure(out var initError))
101  {
102  Console.WriteLine($"EOS failed to initialize: {initError}");
103  }
104  }
105  }
106 
107  //TODO: figure out how consent is supposed to work for servers
108  //Console.WriteLine("Initializing GameAnalytics");
109  //GameAnalyticsManager.InitIfConsented();
110 
111  Console.WriteLine("Initializing GameScreen");
112  GameScreen = new GameScreen();
113 
114  MainThread = Thread.CurrentThread;
115  }
116 
117  public void Init()
118  {
120 
122 
123  ContentPackageManager.Init().Consume();
124  ContentPackageManager.LogEnabledRegularPackageErrors();
125 
127 
128  Screen.SelectNull();
129 
131 
132  CheckContentPackage();
133 
134  LuaCs = new LuaCsSetup();
135  }
136 
137 
138  private void CheckContentPackage()
139  {
141  {
142  DebugConsole.ThrowErrorLocalized(
143  TextManager.GetWithVariables("versionmismatchwarning",
144  ("[gameversion]", Version.ToString()),
145  ("[contentversion]", VanillaContent.GameVersion.ToString())));
146  }
147  }
148 
149  public void StartServer()
150  {
151  string name = "Server";
152  int port = NetConfig.DefaultPort;
153  int queryPort = NetConfig.DefaultQueryPort;
154  bool publiclyVisible = false;
155  string password = "";
156  bool enableUpnp = false;
157 
158  int maxPlayers = 10;
159  Option<int> ownerKey = Option.None;
160  Option<P2PEndpoint> ownerEndpoint = Option.None;
161  IPAddress listenIp = IPAddress.Any;
162 
163  XDocument doc = XMLExtensions.TryLoadXml(ServerSettings.SettingsFile);
164  if (doc?.Root == null)
165  {
166  DebugConsole.AddWarning("File \"" + ServerSettings.SettingsFile + "\" not found. Starting the server with default settings.");
167  }
168  else
169  {
170  name = doc.Root.GetAttributeString(nameof(ServerSettings.Name), "Server");
171  port = doc.Root.GetAttributeInt(nameof(ServerSettings.Port), NetConfig.DefaultPort);
172  queryPort = doc.Root.GetAttributeInt(nameof(ServerSettings.QueryPort), NetConfig.DefaultQueryPort);
173  publiclyVisible = doc.Root.GetAttributeBool(nameof(ServerSettings.IsPublic), false);
174  enableUpnp = doc.Root.GetAttributeBool(nameof(ServerSettings.EnableUPnP), false);
175  maxPlayers = doc.Root.GetAttributeInt(nameof(ServerSettings.MaxPlayers), 10);
176  password = doc.Root.GetAttributeString("password", "");
177  ownerKey = Option<int>.None();
178  }
179 
180 #if DEBUG
181  foreach (string s in CommandLineArgs)
182  {
183  Console.WriteLine(s);
184  }
185 #endif
186 
187  for (int i = 0; i < CommandLineArgs.Length; i++)
188  {
189  switch (CommandLineArgs[i].Trim().ToLowerInvariant())
190  {
191  case "-name":
192  name = CommandLineArgs[i + 1];
193  i++;
194  break;
195  case "-ip":
196  if (IPAddress.TryParse(CommandLineArgs[i + 1], out IPAddress address))
197  listenIp = address;
198  else
199  DebugConsole.ThrowError($"Invalid Ip Address '{CommandLineArgs[i + 1]}'.");
200  break;
201  case "-port":
202  int.TryParse(CommandLineArgs[i + 1], out port);
203  i++;
204  break;
205  case "-queryport":
206  int.TryParse(CommandLineArgs[i + 1], out queryPort);
207  i++;
208  break;
209  case "-public":
210  bool.TryParse(CommandLineArgs[i + 1], out publiclyVisible);
211  i++;
212  break;
213  case "-password":
214  password = CommandLineArgs[i + 1];
215  i++;
216  break;
217  case "-nopassword":
218  password = "";
219  break;
220  case "-upnp":
221  case "-enableupnp":
222  bool.TryParse(CommandLineArgs[i + 1], out enableUpnp);
223  i++;
224  break;
225  case "-maxplayers":
226  int.TryParse(CommandLineArgs[i + 1], out maxPlayers);
227  i++;
228  break;
229  case "-ownerkey":
230  if (int.TryParse(CommandLineArgs[i + 1], out int key))
231  {
232  ownerKey = Option<int>.Some(key);
233  }
234  i++;
235  break;
236  case "-endpoint":
237  ownerEndpoint = P2PEndpoint.Parse(CommandLineArgs[i + 1]);
238  i++;
239  break;
240  case "-pipes":
241  //handled in TryStartChildServerRelay
242  i += 2;
243  break;
244  }
245  }
246 
247  Server = new GameServer(
248  name,
249  listenIp,
250  port,
251  queryPort,
252  publiclyVisible,
253  password,
254  enableUpnp,
255  maxPlayers,
256  ownerKey,
257  ownerEndpoint);
258  Server.StartServer(registerToServerList: true);
259 
260  for (int i = 0; i < CommandLineArgs.Length; i++)
261  {
262  switch (CommandLineArgs[i].Trim().ToLowerInvariant())
263  {
264  case "-playstyle":
265  Enum.TryParse(CommandLineArgs[i + 1], out PlayStyle playStyle);
266  Server.ServerSettings.PlayStyle = playStyle;
267  i++;
268  break;
269  case "-banafterwrongpassword":
270  bool.TryParse(CommandLineArgs[i + 1], out bool banAfterWrongPassword);
271  Server.ServerSettings.BanAfterWrongPassword = banAfterWrongPassword;
272  break;
273  case "-karma":
274  case "-karmaenabled":
275  bool.TryParse(CommandLineArgs[i + 1], out bool karmaEnabled);
276  Server.ServerSettings.KarmaEnabled = karmaEnabled;
277  i++;
278  break;
279  case "-karmapreset":
280  string karmaPresetName = CommandLineArgs[i + 1];
281  Server.ServerSettings.KarmaPreset = karmaPresetName;
282  i++;
283  break;
284  case "-language":
285  LanguageIdentifier language = CommandLineArgs[i + 1].ToLanguageIdentifier();
286  if (ServerLanguageOptions.Options.Any(o => o.Identifier == language))
287  {
288  Server.ServerSettings.Language = language;
289  }
290  i++;
291  break;
292  }
293  }
294  }
295 
296  public void CloseServer()
297  {
298  Server?.Quit();
299  ShouldRun = false;
300  Server = null;
301  }
302 
303  public void Run()
304  {
305  Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Character));
306  Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Item));
307  Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Items.Components.ItemComponent));
308  Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Hull));
309 
310  Init();
311  StartServer();
312 
313  ResetFrameTime();
314 
315  double frequency = (double)Stopwatch.Frequency;
316  if (frequency <= 1500)
317  {
318  DebugConsole.NewMessage("WARNING: Stopwatch frequency under 1500 ticks per second. Expect significant syncing accuracy issues.", Color.Yellow);
319  }
320 
321  Stopwatch performanceCounterTimer = Stopwatch.StartNew();
322 
323  stopwatch = Stopwatch.StartNew();
324  long prevTicks = stopwatch.ElapsedTicks;
325  while (ShouldRun)
326  {
327  long currTicks = stopwatch.ElapsedTicks;
328  double elapsedTime = Math.Max(currTicks - prevTicks, 0) / frequency;
329  Timing.Accumulator += elapsedTime;
330  if (Timing.Accumulator > Timing.AccumulatorMax)
331  {
332  //prevent spiral of death:
333  //if the game's running too slowly then we have no choice but to skip a bunch of steps
334  //otherwise it snowballs and becomes unplayable
335  Timing.Accumulator = Timing.Step;
336  }
337 
338  CrossThread.ProcessTasks();
339 
340  prevTicks = currTicks;
341  while (Timing.Accumulator >= Timing.Step)
342  {
343  performanceCounterTimer.Start();
344 
345  Timing.TotalTime += Timing.Step;
346  Timing.TotalTimeUnpaused += Timing.Step;
347  DebugConsole.Update();
349  {
350  Screen.Selected?.Update((float)Timing.Step);
351  }
352  Server.Update((float)Timing.Step);
353  if (Server == null) { break; }
354  SteamManager.Update((float)Timing.Step);
355  EosInterface.Core.Update();
356  TaskPool.Update();
357  CoroutineManager.Update(paused: false, (float)Timing.Step);
358 
360  performanceCounterTimer.Stop();
362  {
363  GameMain.LuaCs.PerformanceCounter.UpdateElapsedTime = (double)performanceCounterTimer.ElapsedTicks / Stopwatch.Frequency;
364  }
365  performanceCounterTimer.Reset();
366 
367  Timing.Accumulator -= Timing.Step;
368  updateCount++;
369  }
370 
371 #if !DEBUG
372  if (Server?.OwnerConnection == null)
373  {
374  DebugConsole.UpdateCommandLine((int)(Timing.Accumulator * 800));
375  }
376  else
377  {
378  DebugConsole.Clear();
379  }
380 #else
381  DebugConsole.UpdateCommandLine((int)(Timing.Accumulator * 800));
382 #endif
383 
384  int frameTime = (int)((stopwatch.ElapsedTicks - prevTicks) / frequency * 1000.0);
385  frameTime = Math.Max(0, frameTime);
386 
387  Thread.Sleep(Math.Max(((int)(Timing.Step * 1000.0) - frameTime) / 2, 0));
388 
389  if (performanceCounterTimer.ElapsedMilliseconds > 1000)
390  {
391  int updateRate = (int)Math.Round(updateCount / (double)(performanceCounterTimer.ElapsedMilliseconds / 1000.0));
392  prevUpdateRates.Enqueue(updateRate);
393  if (prevUpdateRates.Count >= 10)
394  {
395  int avgUpdateRate = (int)prevUpdateRates.Average();
396  if (avgUpdateRate < Timing.FixedUpdateRate * 0.98 && GameSession != null && GameSession.RoundDuration > 1.0)
397  {
398  DebugConsole.AddWarning($"Running slowly ({avgUpdateRate} updates/s)!");
399  if (Server != null)
400  {
401  foreach (Client c in Server.ConnectedClients)
402  {
404  {
405  Server.SendConsoleMessage($"Server running slowly ({avgUpdateRate} updates/s)!", c, Color.Orange);
406  }
407  }
408  }
409  }
410  prevUpdateRates.Clear();
411  }
412  performanceCounterTimer.Restart();
413  updateCount = 0;
414  }
415  }
416  stopwatch.Stop();
417 
418  CloseServer();
419 
420  SteamManager.ShutDown();
421 
422  SaveUtil.CleanUnnecessarySaveFiles();
423 
424  if (GameSettings.CurrentConfig.SaveDebugConsoleLogs
425  || GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.SaveLogs(); }
426  if (GameAnalyticsManager.SendUserStatistics) { GameAnalyticsManager.ShutDown(); }
427 
428  MainThread = null;
429  }
430 
431  public static void ResetFrameTime()
432  {
433  Timing.Accumulator = 0.0f;
434  stopwatch?.Restart();
435  prevUpdateRates.Clear();
436  updateCount = 0;
437  }
438 
439  public CoroutineHandle ShowLoading(IEnumerable<CoroutineStatus> loader, bool waitKeyHit = true)
440  {
441  return CoroutineManager.StartCoroutine(loader);
442  }
443 
444  public void Exit()
445  {
446  ShouldRun = false;
447  GameMain.LuaCs.Stop();
448  }
449  }
450 }
readonly Version GameVersion
static NetLobbyScreen NetLobbyScreen
Definition: GameMain.cs:57
static ContentPackage VanillaContent
Definition: GameMain.cs:71
readonly string[] CommandLineArgs
Definition: GameMain.cs:74
CoroutineHandle ShowLoading(IEnumerable< CoroutineStatus > loader, bool waitKeyHit=true)
Definition: GameMain.cs:439
static void ResetFrameTime()
Definition: GameMain.cs:431
static Thread MainThread
Definition: GameMain.cs:53
static World World
Definition: GameMain.cs:28
static bool IsMultiplayer
Definition: GameMain.cs:24
static bool IsSingleplayer
Definition: GameMain.cs:23
static readonly Version Version
Definition: GameMain.cs:21
static GameScreen GameScreen
Definition: GameMain.cs:56
static GameServer Server
Definition: GameMain.cs:39
static GameMain Instance
Definition: GameMain.cs:48
static LuaCsSetup LuaCs
Definition: GameMain.cs:37
static bool ShouldRun
Definition: GameMain.cs:64
static GameSession GameSession
Definition: GameMain.cs:45
static readonly Screen SubEditorScreen
Definition: GameMain.cs:62
GameMain(string[] args)
Definition: GameMain.cs:76
virtual bool Paused
Definition: GameMode.cs:36
LuaCsPerformanceCounter PerformanceCounter
Definition: LuaCsSetup.cs:92
NetworkConnection OwnerConnection
Definition: GameServer.cs:135
override IReadOnlyList< Client > ConnectedClients
Definition: GameServer.cs:117
void Update(float deltaTime)
Definition: GameServer.cs:389
void StartServer(bool registerToServerList)
Definition: GameServer.cs:204
void SendConsoleMessage(string txt, Client recipient, Color? color=null)
Definition: GameServer.cs:3659
static Screen Selected
Definition: Screen.cs:5
static void SelectNull()
Definition: Screen.cs:7
virtual void Update(double deltaTime)
Definition: Screen.cs:52
static void RefreshSavedSubs()
static readonly UnimplementedScreen Instance
static new Option< P2PEndpoint > Parse(string str)