1 /**
2 Copyright: Copyright (c) 2016-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.dbg.plugin;
7 
8 import voxelman.log;
9 import std.array;
10 import std.container.rbtree;
11 import pluginlib;
12 import voxelman.core.events;
13 import voxelman.eventdispatcher.plugin;
14 import voxelman.net.plugin;
15 import voxelman.graphics.plugin;
16 import voxelman.text.textformatter;
17 
18 alias DebugGuiHandler = void delegate();
19 
20 enum FPS_ORDER = 0;
21 enum INFO_ORDER = 50;
22 enum DEBUG_ORDER = 100;
23 enum SETTINGS_ORDER = 200;
24 
25 struct DebugGuiHandlerItem
26 {
27 	int order;
28 	string name;
29 	DebugGuiHandler handler;
30 
31 	int opCmp(ref const DebugGuiHandlerItem other) const {
32 		int orderDiff = other.order - order;
33 		if (orderDiff == 0) return other.name < name;
34 		return orderDiff;
35 	}
36 }
37 
38 final class DebugClient : IPlugin
39 {
40 	mixin IdAndSemverFrom!"voxelman.dbg.plugininfo";
41 
42 	Debugger dbg;
43 	NetClientPlugin connection;
44 	RedBlackTree!(DebugGuiHandlerItem, "a>b") handlerList;
45 	LineBuffer* debugText;
46 
47 	override void registerResourceManagers(void delegate(IResourceManager) registerHandler)
48 	{
49 		registerHandler(dbg = new Debugger);
50 	}
51 
52 	override void preInit()
53 	{
54 		handlerList = new typeof(handlerList);
55 		registerDebugGuiHandler(&drawDebugGroup, DEBUG_ORDER, "Debug");
56 	}
57 
58 	override void init(IPluginManager pluginman)
59 	{
60 		auto evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
61 		evDispatcher.subscribeToEvent(&handleDoGuiEvent);
62 		connection = pluginman.getPlugin!NetClientPlugin;
63 		connection.registerPacket!TelemetryPacket(&handleTelemetryPacket);
64 		auto graphics = pluginman.getPlugin!GraphicsPlugin;
65 		debugText = &graphics.debugText;
66 	}
67 
68 	// bigger order items go lower in menu
69 	void registerDebugGuiHandler(DebugGuiHandler handler, int order, string name)
70 	{
71 		handlerList.insert(DebugGuiHandlerItem(order, name, handler));
72 	}
73 
74 	void handleDoGuiEvent(ref DoGuiEvent event)
75 	{
76 		//igSetNextWindowSize(ImVec2(225, 380), ImGuiSetCond_Once);
77 		//igSetNextWindowPos(ImVec2(0, 0), ImGuiSetCond_Once);
78 		//igBegin("Debug");
79 		foreach(item; handlerList[])
80 		{
81 			item.handler();
82 		}
83 		//igEnd();
84 	}
85 
86 	private void drawDebugGroup()
87 	{
88 		//if (igCollapsingHeader("Debug"))
89 		//{
90 		//	foreach(key, var; dbg.vars) {
91 		//		igTextf("%s: %s", key, var);
92 		//	}
93 		//	foreach(key, ref buf; dbg.buffers) {
94 		//		igPlotLines2(key.ptr, &get_val, cast(void*)&buf, cast(int)buf.maxLen, cast(int)buf.next);
95 		//	}
96 		//}
97 
98 		foreach(key, var; dbg.vars) {
99 			debugText.putfln("%s: %s", key, var);
100 		}
101 	}
102 
103 	private void handleTelemetryPacket(ubyte[] packetData)
104 	{
105 		auto packet = unpackPacketNoDup!TelemetryPacket(packetData);
106 		dbg.setVar(packet.name, packet.val);
107 	}
108 }
109 
110 extern(C) float get_val(void* data, int index)
111 {
112 	VarBuffer* buf = cast(VarBuffer*)data;
113 	return buf.vals[index % buf.maxLen];
114 }
115 
116 final class DebugServer : IPlugin
117 {
118 	mixin IdAndSemverFrom!"voxelman.dbg.plugininfo";
119 
120 	Debugger dbg;
121 	NetServerPlugin connection;
122 
123 	override void init(IPluginManager pluginman)
124 	{
125 		auto evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
126 		evDispatcher.subscribeToEvent(&handlePostUpdateEvent);
127 		connection = pluginman.getPlugin!NetServerPlugin;
128 		connection.registerPacket!TelemetryPacket();
129 	}
130 
131 	override void registerResourceManagers(void delegate(IResourceManager) registerHandler)
132 	{
133 		registerHandler(dbg = new Debugger);
134 	}
135 
136 	void handlePostUpdateEvent(ref PostUpdateEvent event)
137 	{
138 		foreach(pair; dbg.vars.byKeyValue())
139 		{
140 			connection.sendToAll(TelemetryPacket(pair.key, pair.value));
141 		}
142 	}
143 }
144 
145 struct VarBuffer
146 {
147 	this(double initVal, size_t _maxLen) {
148 		maxLen = _maxLen;
149 		vals = uninitializedArray!(double[])(maxLen);
150 		vals[0] = initVal;
151 		vals[1..$] = 0;
152 		length = 1;
153 		next = 1;
154 	}
155 	void insert(double val)
156 	{
157 		vals[next] = val;
158 		next = (next + 1) % maxLen;
159 		length = (length + 1) % maxLen;
160 	}
161 	@disable this();
162 	double[] vals;
163 	size_t next;
164 	size_t length;
165 	size_t maxLen;
166 }
167 
168 
169 final class Debugger : IResourceManager
170 {
171 	override string id() @property { return "voxelman.dbg.debugger"; }
172 	VarBuffer[string] buffers;
173 	double[string] vars;
174 
175 	void logVar(string name, double val, size_t maxLen)
176 	{
177 		if (auto buf = name in buffers)
178 			buf.insert(val);
179 		else
180 			buffers[name] = VarBuffer(val, maxLen);
181 	}
182 
183 	void setVar(string name, double val)
184 	{
185 		vars[name] = val;
186 	}
187 
188 	void clearVar(string name)
189 	{
190 		buffers.remove(name);
191 	}
192 }
193 
194 struct TelemetryPacket
195 {
196 	string name;
197 	double val;
198 }