1 /**
2 Copyright: Copyright (c) 2015-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module voxelman.server.plugin;
8 
9 import voxelman.log;
10 import core.time : MonoTime, Duration, msecs, usecs, dur;
11 
12 import pluginlib;
13 
14 import voxelman.core.config;
15 import voxelman.core.events;
16 
17 import voxelman.eventdispatcher.plugin : EventDispatcherPlugin;
18 import voxelman.command.plugin;
19 
20 
21 //version = manualGC;
22 version(manualGC) import core.memory;
23 
24 
25 class ServerPlugin : IPlugin
26 {
27 private:
28 	EventDispatcherPlugin evDispatcher;
29 
30 public:
31 	ServerMode mode;
32 	bool isRunning = false;
33 	bool isAutosaveEnabled = true;
34 	Duration autosavePeriod = dur!"seconds"(10);
35 	MonoTime lastSaveTime;
36 
37 	mixin IdAndSemverFrom!"voxelman.server.plugininfo";
38 
39 	override void init(IPluginManager pluginman)
40 	{
41 		evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
42 
43 		auto commandPlugin = pluginman.getPlugin!CommandPluginServer;
44 		commandPlugin.registerCommand(CommandInfo("sv_stop|stop", &onStopCommand, null, "Stops the server"));
45 		commandPlugin.registerCommand(CommandInfo("save", &onSaveCommand, null, "Saves the world"));
46 	}
47 
48 	void onStopCommand(CommandParams) { isRunning = false; }
49 	void onSaveCommand(CommandParams) { save(); }
50 
51 	void run(string[] args, ServerMode serverMode)
52 	{
53 		import core.thread : Thread;
54 		import core.memory;
55 
56 		mode = serverMode;
57 
58 		infof("Starting game...");
59 		evDispatcher.postEvent(GameStartEvent());
60 		infof("[Running]");
61 
62 		MonoTime prevTime = MonoTime.currTime;
63 		enum size_t SERVER_FRAME_TIME_USECS = 1_000_000 / SERVER_UPDATES_PER_SECOND;
64 		Duration frameTime = SERVER_FRAME_TIME_USECS.usecs;
65 		lastSaveTime = MonoTime.currTime;
66 
67 		version(manualGC) GC.disable();
68 
69 		// Main loop
70 		isRunning = true;
71 		import voxelman.thread.servercontrol : isServerRunning;
72 		while (isRunning && isServerRunning())
73 		{
74 			MonoTime newTime = MonoTime.currTime;
75 			double delta = (newTime - prevTime).total!"usecs" / 1_000_000.0;
76 			prevTime = newTime;
77 
78 			evDispatcher.postEvent(PreUpdateEvent(delta));
79 			evDispatcher.postEvent(UpdateEvent(delta));
80 			evDispatcher.postEvent(PostUpdateEvent(delta));
81 			autosave(MonoTime.currTime);
82 
83 			version(manualGC)
84 			{
85 				if (mode == ServerMode.standalone)
86 				{
87 					auto collectStartTime = MonoTime.currTime;
88 					GC.collect();
89 					GC.minimize();
90 					auto collectDur = MonoTime.currTime - collectStartTime;
91 					//if (collectDur > 50.msecs)
92 					//	infof("GC.collect() time %s", collectDur);
93 				}
94 			}
95 
96 			Duration updateTime = MonoTime.currTime - newTime;
97 			Duration sleepTime = frameTime - updateTime;
98 			if (sleepTime > Duration.zero)
99 				Thread.sleep(sleepTime);
100 		}
101 		infof("Saving...");
102 		save();
103 		infof("Stopping...");
104 		evDispatcher.postEvent(GameStopEvent());
105 	}
106 
107 	void autosave(MonoTime now)
108 	{
109 		if (isAutosaveEnabled && now - lastSaveTime >= autosavePeriod) {
110 			lastSaveTime = now;
111 			save();
112 		}
113 	}
114 
115 	void save()
116 	{
117 		evDispatcher.postEvent(WorldSaveInternalEvent());
118 	}
119 }