1 /**
2 Copyright: Copyright (c) 2016-2017 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 derelict.imgui.imgui;
13 import voxelman.core.events;
14 import voxelman.eventdispatcher.plugin;
15 import voxelman.net.plugin;
16 import voxelman.utils.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 
46 	override void registerResourceManagers(void delegate(IResourceManager) registerHandler)
47 	{
48 		registerHandler(dbg = new Debugger);
49 	}
50 
51 	override void preInit()
52 	{
53 		handlerList = new typeof(handlerList);
54 		registerDebugGuiHandler(&drawDebugGroup, DEBUG_ORDER, "Debug");
55 	}
56 
57 	override void init(IPluginManager pluginman)
58 	{
59 		auto evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
60 		evDispatcher.subscribeToEvent(&handleDoGuiEvent);
61 		connection = pluginman.getPlugin!NetClientPlugin;
62 		connection.registerPacket!TelemetryPacket(&handleTelemetryPacket);
63 	}
64 
65 	// bigger order items go lower in menu
66 	void registerDebugGuiHandler(DebugGuiHandler handler, int order, string name)
67 	{
68 		handlerList.insert(DebugGuiHandlerItem(order, name, handler));
69 	}
70 
71 	void handleDoGuiEvent(ref DoGuiEvent event)
72 	{
73 		igSetNextWindowSize(ImVec2(225, 350), ImGuiSetCond_Once);
74 		igSetNextWindowPos(ImVec2(0, 0), ImGuiSetCond_Once);
75 		igBegin("Debug");
76 		foreach(item; handlerList[])
77 		{
78 			item.handler();
79 		}
80 		igEnd();
81 	}
82 
83 	private void drawDebugGroup()
84 	{
85 		if (igCollapsingHeader("Debug"))
86 		{
87 			foreach(key, var; dbg.vars) {
88 				igTextf("%s: %s", key, var);
89 			}
90 			foreach(key, ref buf; dbg.buffers) {
91 				igPlotLines2(key.ptr, &get_val, cast(void*)&buf, cast(int)buf.maxLen, cast(int)buf.next);
92 			}
93 		}
94 	}
95 
96 	private void handleTelemetryPacket(ubyte[] packetData)
97 	{
98 		auto packet = unpackPacketNoDup!TelemetryPacket(packetData);
99 		dbg.setVar(packet.name, packet.val);
100 	}
101 }
102 
103 extern(C) float get_val(void* data, int index)
104 {
105 	VarBuffer* buf = cast(VarBuffer*)data;
106 	return buf.vals[index % buf.maxLen];
107 }
108 
109 final class DebugServer : IPlugin
110 {
111 	mixin IdAndSemverFrom!"voxelman.dbg.plugininfo";
112 
113 	Debugger dbg;
114 	NetServerPlugin connection;
115 
116 	override void init(IPluginManager pluginman)
117 	{
118 		auto evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
119 		evDispatcher.subscribeToEvent(&handlePostUpdateEvent);
120 		connection = pluginman.getPlugin!NetServerPlugin;
121 		connection.registerPacket!TelemetryPacket();
122 	}
123 
124 	override void registerResourceManagers(void delegate(IResourceManager) registerHandler)
125 	{
126 		registerHandler(dbg = new Debugger);
127 	}
128 
129 	void handlePostUpdateEvent(ref PostUpdateEvent event)
130 	{
131 		foreach(pair; dbg.vars.byKeyValue())
132 		{
133 			connection.sendToAll(TelemetryPacket(pair.key, pair.value));
134 		}
135 	}
136 }
137 
138 struct VarBuffer
139 {
140 	this(double initVal, size_t _maxLen) {
141 		maxLen = _maxLen;
142 		vals = uninitializedArray!(double[])(maxLen);
143 		vals[0] = initVal;
144 		vals[1..$] = 0;
145 		length = 1;
146 		next = 1;
147 	}
148 	void insert(double val)
149 	{
150 		vals[next] = val;
151 		next = (next + 1) % maxLen;
152 		length = (length + 1) % maxLen;
153 	}
154 	@disable this();
155 	double[] vals;
156 	size_t next;
157 	size_t length;
158 	size_t maxLen;
159 }
160 
161 
162 final class Debugger : IResourceManager
163 {
164 	override string id() @property { return "voxelman.dbg.debugger"; }
165 	VarBuffer[string] buffers;
166 	double[string] vars;
167 
168 	void logVar(string name, double val, size_t maxLen)
169 	{
170 		if (auto buf = name in buffers)
171 			buf.insert(val);
172 		else
173 			buffers[name] = VarBuffer(val, maxLen);
174 	}
175 
176 	void setVar(string name, double val)
177 	{
178 		vars[name] = val;
179 	}
180 
181 	void clearVar(string name)
182 	{
183 		buffers.remove(name);
184 	}
185 }
186 
187 struct TelemetryPacket
188 {
189 	string name;
190 	double val;
191 }