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 module voxelman.storage.chunkobservermanager; 7 8 import std.experimental.logger; 9 import dlib.math.vector : ivec3; 10 import netlib.connection : ClientId; 11 import voxelman.storage.coordinates : ChunkWorldPos; 12 import voxelman.storage.volume : Volume, TrisectResult, trisect, calcVolume; 13 14 enum chunkPackLoadSize = 200; 15 16 struct ViewInfo 17 { 18 ClientId clientId; 19 Volume viewVolume; 20 //ivec3 viewRadius; 21 ivec3 observerPosition; 22 int viewRadius; 23 24 size_t numObservedRings; 25 size_t currentChunkIndex; 26 } 27 28 // Manages lists of observers per chunk 29 final class ChunkObserverManager { 30 void delegate(ChunkWorldPos, size_t numObservers) changeChunkNumObservers; 31 void delegate(ChunkWorldPos, ClientId) chunkObserverAdded; 32 size_t delegate() loadQueueSpaceAvaliable; 33 34 private ChunkObservers[ChunkWorldPos] chunkObservers; 35 private ViewInfo[ClientId] viewInfos; 36 37 void update() { 38 39 } 40 string a = q{ 41 42 import std.algorithm : sort; 43 44 if (viewInfos.length == 0) 45 return; 46 47 size_t chunksToLoad = loadQueueSpaceAvaliable(); 48 //infof("queue space %s", chunksToLoad); 49 50 ViewInfo[] infos = viewInfos.values; 51 size_t chunksObserved; 52 53 infinite_loop: 54 while (true) 55 { 56 sort!((a, b) => a.numObservedRings < b.numObservedRings)(infos); 57 chunksObserved = 0; 58 59 foreach(ref info; infos) 60 { 61 immutable size_t currentRing = info.numObservedRings; 62 // fully loaded 63 if (currentRing > info.viewRadius) 64 break; 65 66 //infof("For infos C:%s VR:%s OR:%s CI:%s", info.clientId, info.viewRadius, 67 // info.numObservedRings, info.currentChunkIndex); 68 69 size_t chunksToLoadClient = chunkPackLoadSize; 70 71 immutable ivec3 observerPosition = info.observerPosition; 72 immutable size_t sideSize = currentRing * 2 + 1; 73 immutable size_t sideMax = sideSize - 1; 74 immutable size_t sideSizeSqr = sideSize * sideSize; 75 immutable size_t numIndexes = sideSizeSqr * sideSize; 76 //infof("numIndexes %s sideSize %s", numIndexes, sideSize); 77 78 size_t index = info.currentChunkIndex; 79 ivec3 position; 80 bool empty() { 81 return index == numIndexes; 82 } 83 // returns true if no positions left 84 bool popFront() { 85 size_t x, y, z; 86 while(true) { 87 if (index == numIndexes) 88 return true; 89 90 x = index % sideSize; 91 y = (index / sideSizeSqr) % sideSize; 92 z = (index / sideSize) % sideSize; 93 ++index; 94 95 if (x == 0 || y == 0 || z == 0 || 96 x == sideMax || y == sideMax || z == sideMax) 97 break; 98 } 99 position = ivec3(x, y, z) + observerPosition - ivec3(currentRing, currentRing, currentRing); 100 //infof("popFront %s %s index %s", position, ivec3(x, y, z), index-1); 101 return false; 102 } 103 104 while (true) { 105 bool stop = empty(); 106 if (stop) {// ring end 107 //infof("Ring %s loaded for C:%s", info.numObservedRings, info.clientId); 108 ++info.numObservedRings; 109 info.currentChunkIndex = 0; 110 break; 111 } 112 popFront(); 113 114 bool added = addChunkObserver(ChunkWorldPos(position), info.clientId); 115 116 if (added) { 117 //infof("Add %s", position); 118 --chunksToLoad; 119 --chunksToLoadClient; 120 ++chunksObserved; 121 122 if (chunksToLoad == 0) 123 break infinite_loop; 124 if (chunksToLoadClient == 0) 125 break; 126 } 127 } 128 } 129 // nothing to update 130 if (chunksObserved == 0) 131 break infinite_loop; 132 //else 133 // infof("Observed %s chunks", chunksObserved); 134 } 135 136 foreach(info; infos) { 137 viewInfos[info.clientId] = info; 138 } 139 }; 140 141 ClientId[] getChunkObservers(ChunkWorldPos cwp) { 142 if (auto observers = cwp in chunkObservers) 143 return observers.clients; 144 else 145 return null; 146 } 147 148 void addServerObserver(ChunkWorldPos cwp) { 149 auto list = chunkObservers.get(cwp, ChunkObservers.init); 150 ++list.numServerObservers; 151 changeChunkNumObservers(cwp, list.numObservers); 152 chunkObservers[cwp] = list; 153 } 154 155 void removeServerObserver(ChunkWorldPos cwp) { 156 auto list = chunkObservers.get(cwp, ChunkObservers.init); 157 --list.numServerObservers; 158 changeChunkNumObservers(cwp, list.numObservers); 159 if (list.empty) 160 chunkObservers.remove(cwp); 161 else 162 chunkObservers[cwp] = list; 163 } 164 165 void removeObserver(ClientId clientId) { 166 if (clientId in viewInfos) { 167 changeObserverVolume(clientId, ChunkWorldPos.init, 0); 168 viewInfos.remove(clientId); 169 } 170 else 171 warningf("removing observer %s, that was not added", clientId); 172 } 173 174 void changeObserverVolume(ClientId clientId, ChunkWorldPos observerPosition, int viewRadius) { 175 ViewInfo info = viewInfos.get(clientId, ViewInfo.init); 176 177 Volume oldVolume = info.viewVolume; 178 immutable int size = viewRadius*2 + 1; 179 Volume newVolume = calcVolume(observerPosition, viewRadius); 180 181 if (newVolume == oldVolume) 182 return; 183 184 info = ViewInfo(clientId, newVolume, observerPosition.vector, viewRadius); 185 186 //infof("oldV %s newV %s", oldVolume, newVolume); 187 TrisectResult tsect = trisect(oldVolume, newVolume); 188 189 // remove observer 190 foreach(a; tsect.aPositions) { 191 removeChunkObserver(ChunkWorldPos(a), clientId); 192 //infof("Rem %s", a); 193 } 194 195 // add observer 196 foreach(b; tsect.bPositions) { 197 addChunkObserver(ChunkWorldPos(b), clientId); 198 } 199 200 if (newVolume.empty) 201 viewInfos.remove(clientId); 202 else 203 viewInfos[clientId] = info; 204 } 205 206 private bool addChunkObserver(ChunkWorldPos cwp, ClientId clientId) { 207 auto list = chunkObservers.get(cwp, ChunkObservers.init); 208 if (list.add(clientId)) { 209 changeChunkNumObservers(cwp, list.numObservers); 210 chunkObserverAdded(cwp, clientId); 211 chunkObservers[cwp] = list; 212 return true; 213 } 214 return false; 215 } 216 217 private void removeChunkObserver(ChunkWorldPos cwp, ClientId clientId) { 218 auto list = chunkObservers.get(cwp, ChunkObservers.init); 219 bool removed = list.remove(clientId); 220 if (removed) 221 changeChunkNumObservers(cwp, list.numObservers); 222 if (list.empty) 223 chunkObservers.remove(cwp); 224 else 225 chunkObservers[cwp] = list; 226 } 227 } 228 229 // Describes observers for a single chunk 230 private struct ChunkObservers { 231 // clients observing this chunk 232 private ClientId[] _clients; 233 // ref counts for keeping chunk loaded 234 size_t numServerObservers; 235 236 ClientId[] clients() @property { 237 return _clients; 238 } 239 240 bool empty() @property const { 241 return numObservers == 0; 242 } 243 244 size_t numObservers() @property const { 245 return _clients.length + numServerObservers; 246 } 247 248 bool contains(ClientId clientId) const { 249 import std.algorithm : canFind; 250 return canFind(_clients, clientId); 251 } 252 253 bool add(ClientId clientId) { 254 if (!contains(clientId)) { 255 _clients ~= clientId; 256 return true; 257 } else 258 return false; 259 } 260 261 bool remove(ClientId clientId) { 262 import std.algorithm : remove, SwapStrategy; 263 size_t startLength = _clients.length; 264 _clients = remove!((a) => a == clientId, SwapStrategy.unstable)(_clients); 265 return (startLength - _clients.length) > 0; 266 } 267 }