@@ -34,9 +34,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
@@ -55,8 +53,6 @@
import org.spout.api.generator.WorldGenerator;
import org.spout.api.generator.biome.BiomeManager;
import org.spout.api.generator.biome.EmptyBiomeManager;
import org.spout.api.geo.World;
import org.spout.api.geo.cuboid.Region;
import org.spout.api.geo.discrete.Point;
import org.spout.api.geo.discrete.Transform;
import org.spout.api.io.store.simple.BinaryFileStore;
@@ -65,6 +61,7 @@
import org.spout.api.util.StringMap;
import org.spout.api.util.sanitation.SafeCast;
import org.spout.api.util.sanitation.StringSanitizer;

import org.spout.engine.SpoutEngine;
import org.spout.engine.entity.SpoutEntity;
import org.spout.engine.util.NBTMapper;
@@ -84,7 +81,6 @@
import org.spout.nbt.ShortArrayTag;
import org.spout.nbt.StringTag;
import org.spout.nbt.Tag;
import org.spout.nbt.exception.InvalidTagException;
import org.spout.nbt.stream.NBTInputStream;
import org.spout.nbt.stream.NBTOutputStream;

@@ -101,7 +97,7 @@ public static void saveWorldData(SpoutWorld world) {
generatorName = Long.toHexString(System.currentTimeMillis());
Spout.getEngine().getLogger().severe("Generator name " + generatorName + " is not valid, using " + generatorName + " instead");
}

//Save the world item map
world.getItemMap().save();

@@ -112,7 +108,7 @@ public static void saveWorldData(SpoutWorld world) {
worldTags.put(new StringTag("generator", generatorName));
worldTags.put(new LongTag("UUID_lsb", world.getUID().getLeastSignificantBits()));
worldTags.put(new LongTag("UUID_msb", world.getUID().getMostSignificantBits()));
worldTags.put(new ByteArrayTag("extraData", ((DataMap)world.getDataMap()).getRawMap().compress())); //TODO this tag is named wrong but changing it corrupts worlds...
worldTags.put(new ByteArrayTag("extra_data", ((DataMap) world.getDataMap()).getRawMap().compress())); //TODO this tag is named wrong but changing it corrupts worlds...
worldTags.put(new LongTag("age", world.getAge()));
//World version 2
worldTags.put(new ListTag<FloatTag>("spawn_point", FloatTag.class, NBTMapper.transformToNBT(world.getSpawnPoint())));
@@ -134,23 +130,9 @@ public static void saveWorldData(SpoutWorld world) {
}
}

public static SpoutWorld loadWorldData(Engine engine, String name, WorldGenerator generator, StringMap global) {
public static SpoutWorld loadWorldFromData(String name, WorldGenerator generator, StringMap global) {
SpoutWorld world = null;

String generatorName = generator.getName();
if (!StringSanitizer.isAlphaNumericUnderscore(generatorName)) {
generatorName = Long.toHexString(System.currentTimeMillis());
Spout.getEngine().getLogger().severe("Generator name " + generatorName + " is not valid, using " + generatorName + " instead");
}

//Load the world specific item map
File itemMapFile = new File(new File(SharedFileSystem.WORLDS_DIRECTORY, name), "materials.dat");
BinaryFileStore itemStore = new BinaryFileStore(itemMapFile);
if (itemMapFile.exists()) {
itemStore.load();
}
StringMap itemMap = new StringMap(global, itemStore, 0, Short.MAX_VALUE);

File worldData = new File(new File(SharedFileSystem.WORLDS_DIRECTORY, name), "world.dat");

if (worldData.exists()) {
@@ -159,15 +141,14 @@ public static SpoutWorld loadWorldData(Engine engine, String name, WorldGenerato
is = new NBTInputStream(new DataInputStream(new FileInputStream(worldData)), false);
CompoundTag dataTag = (CompoundTag) is.readTag();
CompoundMap map = dataTag.getValue();
GenericDatatableMap extraData = new GenericDatatableMap();

byte version = SafeCast.toByte(NBTMapper.toTagValue(map.get("version")), WORLD_VERSION);
switch (version) {
case 1:
world = loadVersionOne(engine, name, generator, generatorName, itemMap, map, extraData);
world = loadVersionOne(name, generator, global, map);
break;
case 2:
world = loadVersionOne(engine, name, generator, generatorName, itemMap, map, extraData);
loadVersionTwo(world, map);
world = loadVersionTwo(name, generator, global, map);
break;
}
} catch (Exception e) {
@@ -187,28 +168,74 @@ public static SpoutWorld loadWorldData(Engine engine, String name, WorldGenerato

/**
* Loads version 2 of the world NBT
* @param world The SpoutWorld being loaded
* @param map The compound map of tags
* @param name The name of the world
* @param generator The generator of the world
* @param global The global StringMap for the engine
* @param map The CompoundMap of tags from NBT
* @return The newly created SpoutWorld loaded from NBT
*/
private static void loadVersionTwo(SpoutWorld world, CompoundMap map) {
//Version 2 adds spawn point saving to NBT
Transform spawn = NBTMapper.nbtToTransform(world, (LinkedList<FloatTag>) NBTMapper.toTagValue(map.get("spawn_position")));
private static SpoutWorld loadVersionTwo(String name, WorldGenerator generator, StringMap global, CompoundMap map) {
SpoutWorld world;

//Load the world specific item map
File itemMapFile = new File(new File(SharedFileSystem.WORLDS_DIRECTORY, name), "materials.dat");
BinaryFileStore itemStore = new BinaryFileStore(itemMapFile);
if (itemMapFile.exists()) {
itemStore.load();
}
StringMap itemMap = new StringMap(global, itemStore, 0, Short.MAX_VALUE);

String generatorName = generator.getName();

GenericDatatableMap extraData = new GenericDatatableMap();

long seed = SafeCast.toLong(NBTMapper.toTagValue(map.get("seed")), new Random().nextLong());
String savedGeneratorName = SafeCast.toString(NBTMapper.toTagValue(map.get("generator")), "");

long lsb = SafeCast.toLong(NBTMapper.toTagValue(map.get("UUID_lsb")), new Random().nextLong());
long msb = SafeCast.toLong(NBTMapper.toTagValue(map.get("UUID_msb")), new Random().nextLong());

byte[] extraDataBytes = SafeCast.toByteArray(NBTMapper.toTagValue(map.get("extra_data")), new byte[0]);
extraData.decompress(extraDataBytes);

if (!savedGeneratorName.equals(generatorName)) {
Spout.getLogger().severe("World was saved last with the generator: " + savedGeneratorName + " but is being loaded with: " + generatorName + " MAY CAUSE WORLD CORRUPTION!");
}

long age = SafeCast.toLong(NBTMapper.toTagValue(map.get("age")), 0L);
world = new SpoutWorld(name, Spout.getEngine(), seed, age, generator, new UUID(msb, lsb), itemMap, extraData);

@SuppressWarnings("unchecked")
ArrayList<FloatTag> spawnPosition = SafeCast.toGeneric(NBTMapper.toTagValue(map.get("spawn_position")), null, ArrayList.class);
Transform spawn = NBTMapper.nbtToTransform(world, spawnPosition);
world.setSpawnPoint(spawn);

return world;
}

/**
* Loads version 1 of the world NBT
* @param engine The engine invoking the load
* @param name The name of the world
* @param generator The generator of the world
* @param generatorName The name of the generator
* @param itemMap The StringMap of items for this world
* @param global The global StringMap for the engine
* @param map The CompoundMap of tags from NBT
* @param extraData The Datatable for the world
* @return The newly created SpoutWorld loaded from NBT
*/
private static SpoutWorld loadVersionOne(Engine engine, String name, WorldGenerator generator, String generatorName, StringMap itemMap, CompoundMap map, GenericDatatableMap extraData) {
//Version 1 is basic world tags
private static SpoutWorld loadVersionOne(String name, WorldGenerator generator, StringMap global, CompoundMap map) {
SpoutWorld world;

//Load the world specific item map
File itemMapFile = new File(new File(SharedFileSystem.WORLDS_DIRECTORY, name), "materials.dat");
BinaryFileStore itemStore = new BinaryFileStore(itemMapFile);
if (itemMapFile.exists()) {
itemStore.load();
}
StringMap itemMap = new StringMap(global, itemStore, 0, Short.MAX_VALUE);

String generatorName = generator.getName();

GenericDatatableMap extraData = new GenericDatatableMap();

long seed = SafeCast.toLong(NBTMapper.toTagValue(map.get("seed")), new Random().nextLong());
String savedGeneratorName = SafeCast.toString(NBTMapper.toTagValue(map.get("generator")), "");

@@ -223,20 +250,22 @@ private static SpoutWorld loadVersionOne(Engine engine, String name, WorldGenera
}

long age = SafeCast.toLong(NBTMapper.toTagValue(map.get("age")), 0L);
return new SpoutWorld(name, engine, seed, age, generator, new UUID(msb, lsb), itemMap, extraData);
world = new SpoutWorld(name, Spout.getEngine(), seed, age, generator, new UUID(msb, lsb), itemMap, extraData);

return world;
}

public static void saveChunk(SpoutChunk c, short[] blocks, short[] data, byte[] skyLight, byte[] blockLight, DatatableMap extraData, OutputStream dos) {
CompoundMap chunkTags = new CompoundMap();

//Switch block ids from engine material ids to world specific ids
SpoutWorld world = c.getWorld();
StringMap global = ((SpoutEngine)Spout.getEngine()).getEngineItemMap();
StringMap global = ((SpoutEngine) Spout.getEngine()).getEngineItemMap();
StringMap itemMap = world.getItemMap();
for (int i = 0; i < blocks.length; i++) {
blocks[i] = (short) global.convertTo(itemMap, blocks[i]);
}

chunkTags.put(new ByteTag("version", CHUNK_VERSION));
chunkTags.put(new ByteTag("format", (byte) 0));
chunkTags.put(new IntTag("x", c.getX()));
@@ -279,7 +308,7 @@ public static void saveChunk(SpoutChunk c, short[] blocks, short[] data, byte[]
public static SpoutChunk loadChunk(SpoutRegion r, int x, int y, int z, InputStream dis, ChunkDataForRegion dataForRegion) {
SpoutChunk chunk = null;
NBTInputStream is = null;

try {
if (dis == null) {
//The inputstream is null because no chunk data exists
@@ -299,7 +328,7 @@ public static SpoutChunk loadChunk(SpoutRegion r, int x, int y, int z, InputStre
byte[] skyLight = SafeCast.toByteArray(NBTMapper.toTagValue(map.get("skyLight")), null);
byte[] blockLight = SafeCast.toByteArray(NBTMapper.toTagValue(map.get("blockLight")), null);
byte[] extraData = SafeCast.toByteArray(NBTMapper.toTagValue(map.get("extraData")), null);

BiomeManager manager = null;
if (map.containsKey("biomes")) {
try {
@@ -318,23 +347,24 @@ public static SpoutChunk loadChunk(SpoutRegion r, int x, int y, int z, InputStre
if (manager == null) {
manager = new EmptyBiomeManager(cx, cy, cz);
}

//Convert world block ids to engine material ids
SpoutWorld world = r.getWorld();
StringMap global = ((SpoutEngine)Spout.getEngine()).getEngineItemMap();
StringMap global = ((SpoutEngine) Spout.getEngine()).getEngineItemMap();
StringMap itemMap = world.getItemMap();
for (int i = 0; i < blocks.length; i++) {
blocks[i] = (short) itemMap.convertTo(global, blocks[i]);
}

DatatableMap extraDataMap = new GenericDatatableMap();
extraDataMap.decompress(extraData);

chunk = new FilteredChunk(r.getWorld(), r, cx, cy, cz, populated, blocks, data, skyLight, blockLight, manager, extraDataMap);

CompoundMap entityMap = SafeCast.toGeneric(NBTMapper.toTagValue(map.get("entities")), null, CompoundMap.class);
loadEntities(r, entityMap , dataForRegion.loadedEntities);

loadEntities(r, entityMap, dataForRegion.loadedEntities);

@SuppressWarnings("unchecked")
List<CompoundTag> updateList = SafeCast.toGeneric(NBTMapper.toTagValue(map.get("dynamic_updates")), null, List.class);
loadDynamicUpdates(updateList, dataForRegion.loadedUpdates);
} catch (IOException e) {
@@ -379,63 +409,63 @@ private static SpoutEntity loadEntity(SpoutRegion r, CompoundTag tag) {
CompoundMap map = tag.getValue();

@SuppressWarnings("unused")
byte version = SafeCast.toByte(NBTMapper.toTagValue(map.get("version")), (byte)0);
byte version = SafeCast.toByte(NBTMapper.toTagValue(map.get("version")), (byte) 0);
String name = SafeCast.toString(NBTMapper.toTagValue(map.get("controller")), "");

ControllerType type = ControllerRegistry.get(name);
if (type == null) {
Spout.getEngine().getLogger().log(Level.SEVERE, "No controller type found matching: " + name);
} else if (type.canCreateController()) {

//Read entity
Float pX = SafeCast.toFloat(NBTMapper.toTagValue(map.get("posX")), Float.MAX_VALUE);
Float pY = SafeCast.toFloat(NBTMapper.toTagValue(map.get("posY")), Float.MAX_VALUE);
Float pZ = SafeCast.toFloat(NBTMapper.toTagValue(map.get("posZ")), Float.MAX_VALUE);

if (pX == Float.MAX_VALUE || pY == Float.MAX_VALUE || pZ == Float.MAX_VALUE) {
return null;
}

float sX = SafeCast.toFloat(NBTMapper.toTagValue(map.get("scaleX")), 1.0F);
float sY = SafeCast.toFloat(NBTMapper.toTagValue(map.get("scaleY")), 1.0F);
float sZ = SafeCast.toFloat(NBTMapper.toTagValue(map.get("scaleZ")), 1.0F);

float qX = SafeCast.toFloat(NBTMapper.toTagValue(map.get("quatX")), 0.0F);
float qY = SafeCast.toFloat(NBTMapper.toTagValue(map.get("quatY")), 0.0F);
float qZ = SafeCast.toFloat(NBTMapper.toTagValue(map.get("quatZ")), 0.0F);
float qW = SafeCast.toFloat(NBTMapper.toTagValue(map.get("quatW")), 1.0F);

long msb = SafeCast.toLong(NBTMapper.toTagValue(map.get("UUID_msb")), new Random().nextLong());
long lsb = SafeCast.toLong(NBTMapper.toTagValue(map.get("UUID_lsb")), new Random().nextLong());
UUID uid = new UUID(msb, lsb);

int view = SafeCast.toInt(NBTMapper.toTagValue(map.get("view")), 0);
boolean observer = SafeCast.toGeneric(NBTMapper.toTagValue(map.get("observer")), new ByteTag("", (byte)0), ByteTag.class).getBooleanValue();
boolean observer = SafeCast.toGeneric(NBTMapper.toTagValue(map.get("observer")), new ByteTag("", (byte) 0), ByteTag.class).getBooleanValue();

//Setup controller
Controller controller = type.createController();
try {
boolean controllerDataExists = SafeCast.toGeneric(NBTMapper.toTagValue(map.get("controller_data_exists")), new ByteTag("", (byte)0), ByteTag.class).getBooleanValue();
boolean controllerDataExists = SafeCast.toGeneric(NBTMapper.toTagValue(map.get("controller_data_exists")), new ByteTag("", (byte) 0), ByteTag.class).getBooleanValue();

if (controllerDataExists) {
byte[] data = SafeCast.toByteArray(NBTMapper.toTagValue(map.get("controller_data")), new byte[0]);
DatatableMap dataMap = ((DataMap)controller.data()).getRawMap();
DatatableMap dataMap = ((DataMap) controller.data()).getRawMap();
dataMap.decompress(data);
}
} catch (Exception error) {
Spout.getEngine().getLogger().log(Level.SEVERE, "Unable to load the controller for the type: " + type.getName(), error);
}

//Setup entity
Transform t = new Transform(new Point(r != null ? r.getWorld() : null, pX, pY, pZ), new Quaternion(qX, qY, qZ, qW, false), new Vector3(sX, sY, sZ));
SpoutEntity e = new SpoutEntity((SpoutEngine) Spout.getEngine(), t, controller, view, uid, false);
e.setObserver(observer);

return e;
} else {
Spout.getEngine().getLogger().log(Level.SEVERE, "Unable to create controller for the type: " + type.getName());
}

return null;
}

@@ -452,46 +482,46 @@ private static Tag saveEntity(SpoutEntity e) {
map.put(new FloatTag("posX", e.getPosition().getX()));
map.put(new FloatTag("posY", e.getPosition().getY()));
map.put(new FloatTag("posZ", e.getPosition().getZ()));

map.put(new FloatTag("scaleX", e.getScale().getX()));
map.put(new FloatTag("scaleY", e.getScale().getY()));
map.put(new FloatTag("scaleZ", e.getScale().getZ()));

map.put(new FloatTag("quatX", e.getRotation().getX()));
map.put(new FloatTag("quatY", e.getRotation().getY()));
map.put(new FloatTag("quatZ", e.getRotation().getZ()));
map.put(new FloatTag("quatW", e.getRotation().getW()));

map.put(new LongTag("UUID_msb", e.getUID().getMostSignificantBits()));
map.put(new LongTag("UUID_lsb", e.getUID().getLeastSignificantBits()));

map.put(new IntTag("view", e.getViewDistance()));
map.put(new ByteTag("observer", e.isObserverLive()));

//Write controller
try {
//Call onSave
e.getController().onSave();
//Serialize data
DatatableMap dataMap = ((DataMap)e.getController().data()).getRawMap();
DatatableMap dataMap = ((DataMap) e.getController().data()).getRawMap();
if (!dataMap.isEmpty()) {
map.put(new ByteTag("controller_data_exists", true));
map.put(new ByteArrayTag("controller_data", dataMap.compress()));
} else {
map.put(new ByteTag("controller_data_exists", false));
}
} catch(Exception error) {
} catch (Exception error) {
Spout.getEngine().getLogger().log(Level.SEVERE, "Unable to write the controller information for the type: " + e.getController().getType(), error);
}

CompoundTag tag = new CompoundTag("entity_" + e.getId(), map);

return tag;
}

private static ListTag<CompoundTag> saveDynamicUpdates(SpoutChunk c) {
List<DynamicBlockUpdate> updates = ((SpoutRegion)c.getRegion()).getDynamicBlockUpdates(c);
List<DynamicBlockUpdate> updates = ((SpoutRegion) c.getRegion()).getDynamicBlockUpdates(c);

List<CompoundTag> list = new ArrayList<CompoundTag>(updates.size());

for (DynamicBlockUpdate update : updates) {
@@ -503,18 +533,17 @@ private static ListTag<CompoundTag> saveDynamicUpdates(SpoutChunk c) {

return new ListTag<CompoundTag>("dynamic_updates", CompoundTag.class, list);
}

private static CompoundTag saveDynamicUpdate(DynamicBlockUpdate update) {
CompoundMap map = new CompoundMap();

map.put(new IntTag("packed", update.getPacked()));
map.put(new LongTag("nextUpdate", update.getNextUpdate()));
map.put(new LongTag("lastUpdate", update.getLastUpdate()));

return new CompoundTag("update", map);

}

private static void loadDynamicUpdates(List<CompoundTag> list, List<DynamicBlockUpdate> loadedUpdates) {
if (list != null) {
for (CompoundTag t : list) {
@@ -525,7 +554,7 @@ private static void loadDynamicUpdates(List<CompoundTag> list, List<DynamicBlock
}
}
}

private static DynamicBlockUpdate loadDynamicUpdate(CompoundTag t) {
CompoundMap map = t.getValue();
int packed = SafeCast.toInt(NBTMapper.toTagValue(map.get("packed")), -1);
@@ -1,31 +1,6 @@
/*
* This file is part of Spout.

This comment has been minimized.

Copy link
@lukespragg

lukespragg Jun 6, 2012

Member

Why did you remove the header license?

*
* Copyright (c) 2011-2012, SpoutDev <http://www.spout.org/>
* Spout is licensed under the SpoutDev License Version 1.
*
* Spout is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, 180 days after any changes are published, you can use the
* software, incorporating those changes, under the terms of the MIT license,
* as described in the SpoutDev License Version 1.
*
* Spout is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License,
* the MIT license and the SpoutDev License Version 1 along with this program.
* If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public
* License and see <http://www.spout.org/SpoutDevLicenseV1.txt> for the full license,
* including the MIT license.
*/
package org.spout.engine.util;

import java.util.ArrayList;
import java.util.LinkedList;

import org.spout.api.geo.World;
@@ -46,7 +21,7 @@ public class NBTMapper {
* @param floatsList the list of floats representing a transform.
* @return the constructed transform from NBT tags
*/
public static Transform nbtToTransform(World world, LinkedList<FloatTag> floatsList) {
public static Transform nbtToTransform(World world, ArrayList<FloatTag> floatsList) {

This comment has been minimized.

Copy link
@Raphfrk

Raphfrk Jun 6, 2012

Contributor

Might be best to use List here. Even if someone passes a LinkedList, the method would still work (if slower)

//Position
float px = SafeCast.toFloat(floatsList.get(0), 1f);
float py = SafeCast.toFloat(floatsList.get(1), 85f);
@@ -68,8 +43,8 @@ public static Transform nbtToTransform(World world, LinkedList<FloatTag> floatsL
* @param transform The transform to convert to tags
* @return compound map composing of tags
*/
public static LinkedList<FloatTag> transformToNBT(Transform transform) {
LinkedList<FloatTag> floatsList = new LinkedList<FloatTag>();
public static ArrayList<FloatTag> transformToNBT(Transform transform) {
ArrayList<FloatTag> floatsList = new ArrayList<FloatTag>(10);
Vector3 point = transform.getPosition();
Quaternion rotation = transform.getRotation();
Vector3 scale = transform.getScale();