1 /**
2 Copyright: Copyright (c) 2015-2016 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 std.experimental.logger;
10 import std.datetime : MonoTime, Duration, msecs, usecs, dur;
11 
12 import pluginlib;
13 import pluginlib.pluginmanager;
14 
15 import voxelman.core.config;
16 import voxelman.core.events;
17 
18 import voxelman.eventdispatcher.plugin : EventDispatcherPlugin;
19 import voxelman.command.plugin;
20 
21 
22 //version = manualGC;
23 version(manualGC) import core.memory;
24 
25 shared static this()
26 {
27 	auto s = new ServerPlugin;
28 	pluginRegistry.regServerPlugin(s);
29 	pluginRegistry.regServerMain(&s.run);
30 }
31 
32 struct WorldSaveInternalEvent {}
33 
34 enum ServerMode
35 {
36 	dedicated,
37 	internal
38 }
39 
40 class ServerPlugin : IPlugin
41 {
42 private:
43 	PluginManager pluginman;
44 	EventDispatcherPlugin evDispatcher;
45 
46 public:
47 	ServerMode mode;
48 	bool isRunning = false;
49 	bool isAutosaveEnabled = true;
50 	Duration autosavePeriod = dur!"seconds"(10);
51 	MonoTime lastSaveTime;
52 
53 	mixin IdAndSemverFrom!(voxelman.server.plugininfo);
54 
55 	override void init(IPluginManager pluginman)
56 	{
57 		evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
58 
59 		auto commandPlugin = pluginman.getPlugin!CommandPluginServer;
60 		commandPlugin.registerCommand("sv_stop|stop", &onStopCommand);
61 		commandPlugin.registerCommand("save", &onSaveCommand);
62 	}
63 
64 	void onStopCommand(CommandParams) { isRunning = false; }
65 	void onSaveCommand(CommandParams) { save(); }
66 
67 	void load(string[] args)
68 	{
69 		pluginman = new PluginManager;
70 		// register all plugins and managers
71 		import voxelman.pluginlib.plugininforeader : filterEnabledPlugins;
72 		foreach(p; pluginRegistry.serverPlugins.byValue.filterEnabledPlugins(args))
73 		{
74 			pluginman.registerPlugin(p);
75 		}
76 		// Actual loading sequence
77 		pluginman.initPlugins();
78 	}
79 
80 	void run(string[] args, bool dedicated)
81 	{
82 		import core.thread : Thread, thread_joinAll;
83 		import core.memory;
84 
85 		if (dedicated)
86 			mode = ServerMode.dedicated;
87 		else
88 			mode = ServerMode.internal;
89 
90 		load(args);
91 		infof("Starting game...");
92 		evDispatcher.postEvent(GameStartEvent());
93 		infof("[Running]");
94 
95 		MonoTime prevTime = MonoTime.currTime;
96 		Duration frameTime = SERVER_FRAME_TIME_USECS.usecs;
97 		lastSaveTime = MonoTime.currTime;
98 
99 		version(manualGC) GC.disable();
100 
101 		// Main loop
102 		isRunning = true;
103 		import voxelman.client.servercontrol : isServerRunning;
104 		while (isRunning && isServerRunning())
105 		{
106 			MonoTime newTime = MonoTime.currTime;
107 			double delta = (newTime - prevTime).total!"usecs" / 1_000_000.0;
108 			prevTime = newTime;
109 
110 			evDispatcher.postEvent(PreUpdateEvent(delta));
111 			evDispatcher.postEvent(UpdateEvent(delta));
112 			evDispatcher.postEvent(PostUpdateEvent(delta));
113 			autosave(MonoTime.currTime);
114 
115 			version(manualGC)
116 			{
117 				auto collectStartTime = MonoTime.currTime;
118 				GC.collect();
119 				GC.minimize();
120 				auto collectDur = MonoTime.currTime - collectStartTime;
121 				//if (collectDur > 50.msecs)
122 				//	infof("GC.collect() time %s", collectDur);
123 			}
124 
125 			Duration updateTime = MonoTime.currTime - newTime;
126 			Duration sleepTime = frameTime - updateTime;
127 			if (sleepTime > Duration.zero)
128 				Thread.sleep(sleepTime);
129 		}
130 		infof("Saving...");
131 		save();
132 		infof("Stopping...");
133 		evDispatcher.postEvent(GameStopEvent());
134 
135 		if (mode == ServerMode.dedicated)
136 		{
137 			thread_joinAll();
138 		}
139 		infof("[Stopped]");
140 	}
141 
142 	void autosave(MonoTime now)
143 	{
144 		if (isAutosaveEnabled && now - lastSaveTime >= autosavePeriod) {
145 			lastSaveTime = now;
146 			save();
147 		}
148 	}
149 
150 	void save()
151 	{
152 		evDispatcher.postEvent(WorldSaveInternalEvent());
153 	}
154 }