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 }