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 class ServerPlugin : IPlugin
35 {
36 private:
37 	PluginManager pluginman;
38 	EventDispatcherPlugin evDispatcher;
39 
40 public:
41 	bool isRunning = false;
42 	bool isAutosaveEnabled = true;
43 	Duration autosavePeriod = dur!"seconds"(10);
44 	MonoTime lastSaveTime;
45 
46 	mixin IdAndSemverFrom!(voxelman.server.plugininfo);
47 
48 	override void init(IPluginManager pluginman)
49 	{
50 		evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
51 
52 		auto commandPlugin = pluginman.getPlugin!CommandPluginServer;
53 		commandPlugin.registerCommand("sv_stop|stop", &onStopCommand);
54 		commandPlugin.registerCommand("save", &onSaveCommand);
55 	}
56 
57 	void onStopCommand(CommandParams) { isRunning = false; }
58 	void onSaveCommand(CommandParams) { save(); }
59 
60 	void load(string[] args)
61 	{
62 		pluginman = new PluginManager;
63 		// register all plugins and managers
64 		import voxelman.pluginlib.plugininforeader : filterEnabledPlugins;
65 		foreach(p; pluginRegistry.serverPlugins.byValue.filterEnabledPlugins(args))
66 		{
67 			pluginman.registerPlugin(p);
68 		}
69 		// Actual loading sequence
70 		pluginman.initPlugins();
71 	}
72 
73 	void run(string[] args)
74 	{
75 		import core.thread : Thread, thread_joinAll;
76 		import core.memory;
77 
78 		load(args);
79 		infof("Starting game...");
80 		evDispatcher.postEvent(GameStartEvent());
81 		infof("[Running]");
82 
83 		MonoTime prevTime = MonoTime.currTime;
84 		Duration frameTime = SERVER_FRAME_TIME_USECS.usecs;
85 		lastSaveTime = MonoTime.currTime;
86 
87 		version(manualGC) GC.disable();
88 
89 		// Main loop
90 		isRunning = true;
91 		while (isRunning)
92 		{
93 			MonoTime newTime = MonoTime.currTime;
94 			double delta = (newTime - prevTime).total!"usecs" / 1_000_000.0;
95 			prevTime = newTime;
96 
97 			evDispatcher.postEvent(PreUpdateEvent(delta));
98 			evDispatcher.postEvent(UpdateEvent(delta));
99 			evDispatcher.postEvent(PostUpdateEvent(delta));
100 			autosave(MonoTime.currTime);
101 
102 			version(manualGC)
103 			{
104 				auto collectStartTime = MonoTime.currTime;
105 				GC.collect();
106 				GC.minimize();
107 				auto collectDur = MonoTime.currTime - collectStartTime;
108 				//if (collectDur > 50.msecs)
109 				//	infof("GC.collect() time %s", collectDur);
110 			}
111 
112 			Duration updateTime = MonoTime.currTime - newTime;
113 			Duration sleepTime = frameTime - updateTime;
114 			if (sleepTime > Duration.zero)
115 				Thread.sleep(sleepTime);
116 		}
117 		infof("Saving...");
118 		save();
119 		infof("Stopping...");
120 		evDispatcher.postEvent(GameStopEvent());
121 		thread_joinAll();
122 		infof("[Stopped]");
123 	}
124 
125 	void autosave(MonoTime now)
126 	{
127 		if (isAutosaveEnabled && now - lastSaveTime >= autosavePeriod) {
128 			lastSaveTime = now;
129 			save();
130 		}
131 	}
132 
133 	void save()
134 	{
135 		evDispatcher.postEvent(WorldSaveInternalEvent());
136 	}
137 }