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.world.plugin;
7 
8 import netlib;
9 import pluginlib;
10 
11 import voxelman.core.config : BlockType;
12 import voxelman.core.events : PreUpdateEvent, PostUpdateEvent, GameStopEvent;
13 import voxelman.core.blockman;
14 
15 import voxelman.config.configmanager : ConfigOption, ConfigManager;
16 import voxelman.eventdispatcher.plugin : EventDispatcherPlugin;
17 import voxelman.net.plugin : NetServerPlugin;
18 
19 import voxelman.storage.chunk;
20 import voxelman.storage.chunkmanager;
21 import voxelman.storage.chunkobservermanager;
22 import voxelman.storage.chunkprovider;
23 import voxelman.storage.chunkstorage;
24 import voxelman.storage.coordinates;
25 import voxelman.storage.volume;
26 import voxelman.storage.world;
27 
28 shared static this()
29 {
30 	//pluginRegistry.regClientPlugin(new ClientWorld);
31 	pluginRegistry.regServerPlugin(new ServerWorld);
32 }
33 
34 final class WorldAccess {
35 	private ChunkManager* chunkManager;
36 
37 	this(ChunkManager* chunkManager) {
38 		this.chunkManager = chunkManager;
39 	}
40 
41 	bool setBlock(BlockWorldPos bwp, BlockType blockId) {
42 		auto blockIndex = BlockChunkIndex(bwp);
43 		auto chunkPos = ChunkWorldPos(bwp);
44 		BlockType[] blocks = chunkManager.getWriteBuffer(chunkPos);
45 		if (blocks is null)
46 			return false;
47 		blocks[blockIndex] = blockId;
48 
49 		import std.range : only;
50 		chunkManager.onBlockChanges(chunkPos, only(BlockChange(blockIndex.index, blockId)));
51 		return true;
52 	}
53 
54 	BlockType getBlock(BlockWorldPos bwp) {
55 		auto blockIndex = BlockChunkIndex(bwp);
56 		auto chunkPos = ChunkWorldPos(bwp);
57 		auto snap = chunkManager.getChunkSnapshot(chunkPos);
58 		if (!snap.isNull) {
59 			return snap.blockData.getBlockType(blockIndex);
60 		}
61 		return 0;
62 	}
63 
64 	bool isFree(BlockWorldPos bwp) {
65 		 return getBlock(bwp) < 2; // air or unknown
66 	}
67 }
68 
69 final class ServerWorld : IPlugin
70 {
71 private:
72 	EventDispatcherPlugin evDispatcher;
73 	NetServerPlugin connection;
74 
75 	ConfigOption saveDirOpt;
76 	ConfigOption worldNameOpt;
77 	ConfigOption numWorkersOpt;
78 
79 public:
80 	ChunkManager chunkManager;
81 	ChunkProvider chunkProvider;
82 	ChunkObserverManager chunkObserverManager;
83 
84 	World world;
85 	WorldAccess worldAccess;
86 	BlockMan blockMan;
87 
88 	mixin IdAndSemverFrom!(voxelman.world.plugininfo);
89 
90 	override void registerResourceManagers(void delegate(IResourceManager) registerHandler) {}
91 
92 	override void registerResources(IResourceManagerRegistry resmanRegistry)
93 	{
94 		ConfigManager config = resmanRegistry.getResourceManager!ConfigManager;
95 		saveDirOpt = config.registerOption!string("save_dir", "../../saves");
96 		worldNameOpt = config.registerOption!string("world_name", "world");
97 		numWorkersOpt = config.registerOption!uint("num_workers", 4);
98 	}
99 
100 	override void preInit()
101 	{
102 		chunkManager = new ChunkManager();
103 		worldAccess = new WorldAccess(&chunkManager);
104 		chunkObserverManager = new ChunkObserverManager();
105 
106 		// Component connections
107 		chunkManager.loadChunkHandler = &chunkProvider.loadChunk;
108 		chunkManager.saveChunkHandler = &chunkProvider.saveChunk;
109 
110 		chunkProvider.onChunkLoadedHandlers ~= &chunkManager.onSnapshotLoaded;
111 		chunkProvider.onChunkSavedHandlers ~= &chunkManager.onSnapshotSaved;
112 
113 		chunkObserverManager.changeChunkNumObservers = &chunkManager.setExternalChunkUsers;
114 		chunkObserverManager.chunkObserverAdded = &onChunkObserverAdded;
115 		chunkObserverManager.loadQueueSpaceAvaliable = &chunkProvider.loadQueueSpaceAvaliable;
116 
117 		chunkManager.onChunkLoadedHandlers ~= &onChunkLoaded;
118 		chunkManager.chunkChangesHandlers ~= &sendChanges;
119 
120 		blockMan.loadBlockTypes();
121 		auto worldDir = saveDirOpt.get!string ~ "/" ~ worldNameOpt.get!string;
122 		chunkProvider.init(worldDir, numWorkersOpt.get!uint);
123 		world.init(worldDir);
124 		world.load();
125 	}
126 
127 	override void init(IPluginManager pluginman)
128 	{
129 		evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
130 		evDispatcher.subscribeToEvent(&handlePreUpdateEvent);
131 		evDispatcher.subscribeToEvent(&handlePostUpdateEvent);
132 		evDispatcher.subscribeToEvent(&handleStopEvent);
133 
134 		import voxelman.core.packets : PlaceBlockPacket;
135 		connection = pluginman.getPlugin!NetServerPlugin;
136 		connection.registerPacketHandler!PlaceBlockPacket(&handlePlaceBlockPacket);
137 	}
138 
139 	override void postInit() {}
140 
141 	void handlePreUpdateEvent(ref PreUpdateEvent event)
142 	{
143 		chunkProvider.update();
144 		chunkObserverManager.update();
145 		world.update();
146 	}
147 
148 	void handlePostUpdateEvent(ref PostUpdateEvent event)
149 	{
150 		chunkManager.commitSnapshots(world.currentTimestamp);
151 		chunkManager.sendChanges();
152 	}
153 
154 	void handleStopEvent(ref GameStopEvent event)
155 	{
156 		chunkProvider.stop();
157 		world.save();
158 	}
159 
160 	void onChunkObserverAdded(ChunkWorldPos cwp, ClientId clientId)
161 	{
162 		import voxelman.core.packets : ChunkDataPacket;
163 		auto snap = chunkManager.getChunkSnapshot(cwp);
164 		if (!snap.isNull) {
165 			connection.sendTo(clientId, ChunkDataPacket(cwp.vector, snap.blockData));
166 		}
167 	}
168 
169 	void onChunkLoaded(ChunkWorldPos cwp, BlockDataSnapshot snap)
170 	{
171 		import voxelman.core.packets : ChunkDataPacket;
172 		connection.sendTo(
173 			chunkObserverManager.getChunkObservers(cwp),
174 			ChunkDataPacket(cwp.vector, snap.blockData));
175 	}
176 
177 	void sendChanges(BlockChange[][ChunkWorldPos] changes)
178 	{
179 		import voxelman.core.packets : MultiblockChangePacket;
180 		foreach(pair; changes.byKeyValue)
181 		{
182 			connection.sendTo(
183 				chunkObserverManager.getChunkObservers(pair.key),
184 				MultiblockChangePacket(pair.key.vector, pair.value));
185 		}
186 	}
187 
188 	void handlePlaceBlockPacket(ubyte[] packetData, ClientId clientId)
189 	{
190 		import voxelman.core.packets : PlaceBlockPacket;
191 		//if (serverPlugin.isLoggedIn(clientId))
192 		{
193 			auto packet = unpackPacket!PlaceBlockPacket(packetData);
194 			worldAccess.setBlock(BlockWorldPos(packet.blockPos), packet.blockType);
195 		}
196 	}
197 }