1 /** 2 Copyright: Copyright (c) 2017-2018 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module railroad.wagon.wagon; 7 8 import voxelman.log; 9 import pluginlib; 10 import datadriven; 11 import voxelman.math; 12 13 import voxelman.core.config; 14 import voxelman.entity.plugin; 15 import voxelman.net.plugin; 16 import voxelman.world.storage; 17 import voxelman.world.mesh.utils : FaceSide, oppFaceSides; 18 19 import railroad.rail.railgraph; 20 import railroad.rail.utils; 21 import railroad.wagon.packets; 22 23 @Component("railroad.wagon.client", Replication.toClient) 24 struct WagonClientComponent 25 { 26 vec3 dimPos; 27 DimensionId dimension; 28 } 29 30 @Component("railroad.wagon.server", Replication.toDb) 31 struct WagonServerComponent 32 { 33 RailPos railTilePos; 34 RailSegment currentSegment; 35 36 // 0 or 1 37 // 0 - from point 1 to point 0 38 // 1 - from point 0 to point 1 39 ubyte targetConnection; 40 41 float segmentPos = 0; // [0; segmentLength] 42 float speed = 5; 43 44 vec3 dimPosition() 45 { 46 // [0; 1] 47 float curSegmentProgress = segmentPos / segmentLengths[currentSegment]; 48 49 auto sides = segmentInfos[currentSegment].sides; 50 FaceSide startSide = sides[1 - targetConnection]; 51 FaceSide endSide = sides[targetConnection]; 52 53 vec3 startPoint = railTileConnectionPoints[startSide]; 54 vec3 endPoint = railTileConnectionPoints[endSide]; 55 vec3 wagonTilePos = lerp(startPoint, endPoint, curSegmentProgress); 56 vec3 wagonDimPos = vec3(railTilePos.toBlockWorldPos.xyz) + wagonTilePos; 57 return wagonDimPos; 58 } 59 60 ChunkWorldPos chunk(vec3 wagonDimPos) 61 { 62 auto wagonBlock = BlockWorldPos(wagonDimPos, railTilePos.w); 63 auto wagonChunk = ChunkWorldPos(wagonBlock); 64 return wagonChunk; 65 } 66 67 ChunkWorldPos chunk() 68 { 69 return chunk(dimPosition); 70 } 71 } 72 73 struct WagonPos 74 { 75 RailPos railTilePos; 76 RailSegment currentSegment; 77 78 // 0 or 1 79 // 0 - from point 1 to point 0 80 // 1 - from point 0 to point 1 81 ubyte targetConnection; 82 83 float segmentPos = 0; // [0; segmentLength] 84 85 vec3 dimPosition() 86 { 87 // [0; 1] 88 float curSegmentProgress = segmentPos / segmentLengths[currentSegment]; 89 90 auto sides = segmentInfos[currentSegment].sides; 91 FaceSide startSide = sides[1 - targetConnection]; 92 FaceSide endSide = sides[targetConnection]; 93 94 vec3 startPoint = railTileConnectionPoints[startSide]; 95 vec3 endPoint = railTileConnectionPoints[endSide]; 96 vec3 wagonTilePos = lerp(startPoint, endPoint, curSegmentProgress); 97 vec3 wagonDimPos = vec3(railTilePos.toBlockWorldPos.xyz) + wagonTilePos; 98 return wagonDimPos; 99 } 100 } 101 102 struct WagonLogicServer 103 { 104 import datadriven; 105 EntityManager* eman; 106 EntityPluginServer entityPlugin; 107 RailGraph* railGraph; 108 109 void registerResources(IResourceManagerRegistry resmanRegistry) 110 { 111 auto components = resmanRegistry.getResourceManager!EntityComponentRegistry; 112 eman = components.eman; 113 eman.registerComponent!WagonClientComponent(); 114 eman.registerComponent!WagonServerComponent(); 115 } 116 117 void handleCreateWagonPacket(ubyte[] packetData, SessionId sessionId) 118 { 119 auto packet = unpackPacket!CreateWagonPacket(packetData); 120 createWagon(packet.pos); 121 } 122 123 void createWagon(RailPos pos) 124 { 125 auto rail = pos in railGraph.rails; 126 127 if (rail && !rail.empty) 128 { 129 RailSegment segment; 130 foreach(s; rail.getSegments) { 131 segment = s; 132 break; 133 } 134 135 EntityId eid = eman.eidMan.nextEntityId; 136 infof("create wagon %s at %s", eid, pos); 137 auto wagon = WagonServerComponent(pos, segment); 138 eman.set(eid, wagon); 139 entityPlugin.entityObserverManager.addEntity(eid, wagon.chunk); 140 } 141 } 142 143 void removeWagons() 144 { 145 foreach(EntityId eid; eman.getComponentStorage!WagonServerComponent.byKey) 146 entityPlugin.entityObserverManager.removeEntity(eid); 147 148 eman.getComponentStorage!WagonServerComponent.removeAll; 149 } 150 151 void process(ref ProcessComponentsEvent event) 152 { 153 auto query = eman.query!WagonServerComponent(); 154 foreach(id, wagonServer; query) 155 { 156 moveWagon(id, *wagonServer, event.deltaTime); 157 } 158 } 159 160 void moveWagon(EntityId eid, ref WagonServerComponent wagon, float dt) 161 { 162 float distance = wagon.speed * dt; 163 164 while (true) 165 { 166 float segmentLength = segmentLengths[wagon.currentSegment]; 167 float currentSegmentRemained = segmentLength - wagon.segmentPos; 168 169 if (distance <= currentSegmentRemained) 170 { 171 wagon.segmentPos += distance; 172 break; 173 } 174 else 175 { 176 distance -= currentSegmentRemained; 177 moveToNextSegment(wagon); 178 } 179 } 180 181 auto wagonDimPos = wagon.dimPosition; 182 //infof("wagon %s %s", eid, wagon); 183 184 entityPlugin.entityObserverManager.updateEntityPos(eid, wagon.chunk(wagonDimPos)); 185 eman.set(eid, WagonClientComponent(wagonDimPos, wagon.railTilePos.w)); 186 } 187 188 void moveToNextSegment(ref WagonServerComponent wagon) 189 { 190 auto sides = segmentInfos[wagon.currentSegment].sides; 191 FaceSide targetDirection = sides[wagon.targetConnection]; // 0-3 192 RailPos nextPos = wagon.railTilePos.posInDirection(targetDirection); 193 //infof("next rail %s targetDirection %s", nextPos, targetDirection); 194 195 auto rail = nextPos in railGraph.rails; 196 197 if (rail) 198 { 199 wagon.railTilePos = nextPos; 200 auto startSide = oppFaceSides[targetDirection]; 201 auto avaliableSegments = rail.getSegmentsFromSide(startSide); 202 203 auto length = avaliableSegments.data.length; 204 if (length) 205 { 206 import std.random : uniform; 207 wagon.currentSegment = avaliableSegments[uniform(0, length)]; 208 wagon.segmentPos = 0; 209 wagon.targetConnection = cast(ubyte)(1 - segmentInfos[wagon.currentSegment].sideIndicies[startSide]); 210 return; 211 } 212 } 213 214 // return in opposite direction 215 wagon.segmentPos = 0; 216 wagon.targetConnection = cast(ubyte)(1 - wagon.targetConnection); 217 } 218 }