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 }