Skip to content

Commit e463aac

Browse files
committed
Update .CW importer and World Loading
Now imports environment settings and block definitions. Making a new or updating a current world with an existing world will now copy the environment settings and block definitions of the world being copied to the new/updated world.
1 parent 2d62228 commit e463aac

File tree

5 files changed

+255
-33
lines changed

5 files changed

+255
-33
lines changed

fCraft/Commands/WorldCommands.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,6 +2454,8 @@ static World WorldLoadAdd( CommandReader cmd, Player player, Map map, string nam
24542454
World world;
24552455
try {
24562456
world = WorldManager.AddWorld(player, name, map, false);
2457+
World oldWorld = WorldManager.FindWorldExact(fileName);
2458+
if (oldWorld != null) world.CopyENV(oldWorld);
24572459
} catch (WorldOpException ex) {
24582460
player.Message("WLoad: {0}", ex.Message);
24592461
return null;
@@ -2505,6 +2507,8 @@ static void WorldLoadReplace( CommandReader cmd, Player player, World world,
25052507

25062508
try {
25072509
world.MapChangedBy = player.Name;
2510+
World oldWorld = WorldManager.FindWorldExact(fileName);
2511+
if (oldWorld != null) world.CopyENV(oldWorld);
25082512
world.ChangeMap(map);
25092513
} catch (WorldOpException ex) {
25102514
Logger.Log(LogType.Error, "Could not complete WorldLoad operation: {0}", ex.Message);

fCraft/MapConversion/MapCW.cs

Lines changed: 154 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.IO.Compression;
55
using System.Text;
66
using JetBrains.Annotations;
7+
using ServiceStack.Text;
78

89
namespace fCraft.MapConversion {
910
/// <summary> ClassicWorld map conversion implementation, for converting ClassicWorld map format into fCraft's default map format. </summary>
@@ -81,7 +82,8 @@ public Map Load( string fileName ) {
8182
map.Spawn = new Position {
8283
X = (spawnTag["X"].GetShort() * 32),
8384
Y = (spawnTag["Z"].GetShort() * 32),
84-
Z = (spawnTag["Y"].GetShort() * 32),
85+
//I think a negative Player.CharacterHeight is being used somewhere and is breaking spawn height
86+
Z = (spawnTag["Y"].GetShort() * 32)+Player.CharacterHeight,
8587
R = spawnTag["H"].GetByte(),
8688
L = spawnTag["P"].GetByte(),
8789
};
@@ -110,8 +112,159 @@ public Map Load( string fileName ) {
110112
}
111113

112114
map.Blocks = root["BlockArray"].GetBytes();
115+
if (root.Contains("Metadata")) ReadMetadata(root["Metadata"], map, fileName);
113116
return map;
114117
}
115118
}
119+
void ReadMetadata(NBTag root, Map map, String fileName) {
120+
if (!root.Contains("CPE"))
121+
return;
122+
NBTag cpe = root["CPE"];
123+
124+
if (cpe.Contains("EnvWeatherType"))
125+
map.Metadata.Add("CPE", "Weather", cpe["EnvWeatherType"]["WeatherType"].GetByte().ToString());
126+
if (cpe.Contains("ClickDistance"))
127+
map.Metadata.Add("CPE", "ClickDistance", cpe["ClickDistance"]["Distance"].GetShort().ToString());
128+
if (cpe.Contains("EnvMapAppearance"))
129+
ParseEnvMapAppearance(cpe, map);
130+
if (cpe.Contains("EnvColors"))
131+
ParseEnvColors(cpe, map);
132+
if (cpe.Contains("BlockDefinitions"))
133+
ParseBlockDefinitions(cpe, map, fileName);
134+
}
135+
136+
137+
static void ParseEnvMapAppearance(NBTag cpe, Map map) {
138+
map.Metadata.Add("CPE", "HasEnvMapAppearance", "true");
139+
NBTag comp = cpe["EnvMapAppearance"];
140+
map.Metadata.Add("CPE", "HorizonBlock", comp["EdgeBlock"].GetByte().ToString());
141+
map.Metadata.Add("CPE", "EdgeBlock", comp["SideBlock"].GetByte().ToString());
142+
map.Metadata.Add("CPE", "EdgeLevel", comp["SideLevel"].GetShort().ToString());
143+
if (!comp.Contains("TextureURL"))
144+
return;
145+
146+
string url = comp["TextureURL"].GetString();
147+
map.Metadata.Add("CPE", "Texture", url == Server.DefaultTerrain ? "default" : url);
148+
}
149+
static void ParseEnvColors(NBTag cpe, Map map) {
150+
map.Metadata.Add("CPE", "HasEnvColors", "true");
151+
NBTag comp = cpe["EnvColors"];
152+
map.Metadata.Add("CPE", "SkyColor", GetColor(comp, "Sky"));
153+
map.Metadata.Add("CPE", "CloudColor", GetColor(comp, "Cloud"));
154+
map.Metadata.Add("CPE", "FogColor", GetColor(comp, "Fog"));
155+
map.Metadata.Add("CPE", "LightColor", GetColor(comp, "Sunlight"));
156+
map.Metadata.Add("CPE", "ShadowColor", GetColor(comp, "Ambient"));
157+
}
158+
159+
static string GetColor(NBTag comp, string type) {
160+
if (!comp.Contains(type))
161+
return "";
162+
NBTag rgb = comp[type];
163+
short r = rgb["R"].GetShort(), g = rgb["G"].GetShort(), b = rgb["B"].GetShort();
164+
165+
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
166+
return "";
167+
return r.ToString("X2") + g.ToString("X2") + b.ToString("X2");
168+
}
169+
170+
public static BlockDefinition[] blockDefs = new BlockDefinition[256];
171+
172+
static void ParseBlockDefinitions(NBTag cpe, Map map, String fileName) {
173+
NBTag blocks = cpe["BlockDefinitions"];
174+
bool hasBlockDefs = false;
175+
blockDefs = new BlockDefinition[256];
176+
177+
foreach (NBTag tag in blocks) {
178+
if (tag.Type != NBTType.Compound) continue;
179+
180+
NBTag props = tag;
181+
BlockDefinition def = new BlockDefinition();
182+
def.BlockID = props["ID"].GetByte();
183+
// can't change "ID" to short since backwards compatibility
184+
if (props.Contains("ID2")) {
185+
ushort tempID = (ushort)props["ID2"].GetShort();
186+
if (tempID >= 256) continue;
187+
def.BlockID = (byte)tempID;
188+
}
189+
190+
def.Name = props["Name"].GetString();
191+
def.CollideType = props["CollideType"].GetByte();
192+
def.Speed = props["Speed"].GetFloat();
193+
194+
def.BlocksLight = props["TransmitsLight"].GetByte() == 0;
195+
def.WalkSound = props["WalkSound"].GetByte();
196+
def.FullBright = props["FullBright"].GetByte() != 0;
197+
def.Shape = props["Shape"].GetByte();
198+
def.BlockDraw = props["BlockDraw"].GetByte();
199+
200+
byte[] fog = props["Fog"].GetBytes();
201+
def.FogDensity = fog[0];
202+
// Fix for older ClassicalSharp versions which saved wrong value for density = 0
203+
if (def.FogDensity == 0xFF)
204+
def.FogDensity = 0;
205+
def.FogR = fog[1];
206+
def.FogG = fog[2];
207+
def.FogB = fog[3];
208+
209+
byte[] tex = props["Textures"].GetBytes();
210+
def.TopTex = tex[0];
211+
def.BottomTex = tex[1];
212+
def.LeftTex = tex[2];
213+
def.RightTex = tex[3];
214+
def.FrontTex = tex[4];
215+
def.BackTex = tex[5];
216+
217+
byte[] coords = props["Coords"].GetBytes();
218+
def.MinX = coords[0];
219+
def.MinZ = coords[1];
220+
def.MinY = coords[2];
221+
def.MaxX = coords[3];
222+
def.MaxZ = coords[4];
223+
def.MaxY = coords[5];
224+
225+
// Don't define level custom block if same as global custom block
226+
if (PropsEquals(def, BlockDefinition.GlobalDefs[def.BlockID]))
227+
continue;
228+
229+
blockDefs[def.BlockID] = def;
230+
hasBlockDefs = true;
231+
}
232+
233+
if (hasBlockDefs) {
234+
BlockDefinition[] realDefs = new BlockDefinition[256];
235+
int count = 0;
236+
for (int i = 0;i < 256;i++) {
237+
if (blockDefs[i] == BlockDefinition.GlobalDefs[i]) realDefs[i] = null;
238+
else {
239+
count++;
240+
realDefs[i] = blockDefs[i];
241+
}
242+
}
243+
blockDefs = realDefs;
244+
245+
string path = Paths.BlockDefsDirectory;
246+
path = Path.Combine(path, Path.GetFileName(fileName) + ".txt");
247+
map.Metadata["CPE", "HasBlockDefFile"] = "true";
248+
map.Metadata["CPE", "BlockDefFileName"] = Path.GetFileName(fileName);
249+
try {
250+
using (Stream s = File.Create(path))
251+
JsonSerializer.SerializeToStream(blockDefs, s);
252+
}
253+
catch (Exception ex) {
254+
Logger.Log(LogType.Error, "BlockDefinitions.Save: " + ex);
255+
}
256+
}
257+
}
258+
static bool PropsEquals(BlockDefinition a, BlockDefinition b) {
259+
if (b == null || b.Name == null)
260+
return false;
261+
return a.Name == b.Name && a.CollideType == b.CollideType && a.Speed == b.Speed && a.TopTex == b.TopTex
262+
&& a.BottomTex == b.BottomTex && a.BlocksLight == b.BlocksLight && a.WalkSound == b.WalkSound
263+
&& a.FullBright == b.FullBright && a.Shape == b.Shape && a.BlockDraw == b.BlockDraw
264+
&& a.FogDensity == b.FogDensity && a.FogR == b.FogR && a.FogG == b.FogG && a.FogB == b.FogB
265+
&& a.MinX == b.MinX && a.MinY == b.MinY && a.MinZ == b.MinZ && a.MaxX == b.MaxX
266+
&& a.MaxY == b.MaxY && a.MaxZ == b.MaxZ && a.LeftTex == b.LeftTex && a.RightTex == b.RightTex
267+
&& a.FrontTex == b.FrontTex && a.BackTex == b.BackTex;
268+
}
116269
}
117270
}

fCraft/MapConversion/NBTag.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,10 @@ public static NBTag ReadTag( [NotNull] BinaryReader reader, NBTType type, string
187187
return new NBTag( NBTType.Long, name, IPAddress.NetworkToHostOrder( reader.ReadInt64() ), parent );
188188

189189
case NBTType.Float:
190-
return new NBTag( NBTType.Float, name, reader.ReadSingle(), parent );
190+
byte[] floatBytes = reader.ReadBytes(4);
191+
Array.Reverse(floatBytes);
192+
float value = BitConverter.ToSingle(floatBytes, 0);
193+
return new NBTag( NBTType.Float, name, value, parent );
191194

192195
case NBTType.Double:
193196
return new NBTag( NBTType.Double, name, reader.ReadDouble(), parent );

fCraft/World/World.cs

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -244,28 +244,11 @@ public World ChangeMap( [NotNull] Map newMap ) {
244244
LoadedOn = LoadedOn,
245245
MapChangedBy = MapChangedBy,
246246
MapChangedOn = DateTime.UtcNow,
247-
FogColor = FogColor,
248-
CloudColor = CloudColor,
249-
SkyColor = SkyColor,
250-
EdgeLevel = EdgeLevel,
251-
SidesOffset = SidesOffset,
252-
CloudsHeight = CloudsHeight,
253-
MaxFogDistance = MaxFogDistance,
254-
EdgeBlock = EdgeBlock,
255-
HorizonBlock = HorizonBlock,
256-
LightColor = LightColor,
257-
ShadowColor = ShadowColor,
258-
Weather = Weather,
259-
Texture = Texture,
260-
CloudsSpeed = CloudsSpeed,
261-
WeatherSpeed = WeatherSpeed,
262-
WeatherFade = WeatherFade,
263-
SkyboxHorSpeed = SkyboxHorSpeed,
264-
SkyboxVerSpeed = SkyboxVerSpeed,
265247
Buildable = Buildable,
266248
Deletable = Deletable,
267-
BlockDefs = BlockDefs,
268249
};
250+
newWorld.CopyENV(this);
251+
WorldManager.ParseCPEMetaData(ref newWorld, newMap, Name);
269252
newMap.World = newWorld;
270253
newWorld.Map = newMap;
271254
newWorld.Preload = preload;
@@ -282,6 +265,37 @@ public World ChangeMap( [NotNull] Map newMap ) {
282265
}
283266
}
284267

268+
public void CopyENV(World otherWorld) {
269+
Map = map;
270+
FogColor = otherWorld.FogColor;
271+
CloudColor = otherWorld.CloudColor;
272+
SkyColor = otherWorld.SkyColor;
273+
EdgeLevel = otherWorld.EdgeLevel;
274+
SidesOffset = otherWorld.SidesOffset;
275+
CloudsHeight = otherWorld.CloudsHeight;
276+
MaxFogDistance = otherWorld.MaxFogDistance;
277+
EdgeBlock = otherWorld.EdgeBlock;
278+
HorizonBlock = otherWorld.HorizonBlock;
279+
LightColor = otherWorld.LightColor;
280+
ShadowColor = otherWorld.ShadowColor;
281+
Weather = otherWorld.Weather;
282+
Texture = otherWorld.Texture;
283+
CloudsSpeed = otherWorld.CloudsSpeed;
284+
WeatherSpeed = otherWorld.WeatherSpeed;
285+
WeatherFade = otherWorld.WeatherFade;
286+
SkyboxHorSpeed = otherWorld.SkyboxHorSpeed;
287+
SkyboxVerSpeed = otherWorld.SkyboxVerSpeed;
288+
foreach (Player p in Players) {
289+
BlockDefinition.SendNowRemoveOldBlocks(p, this);
290+
}
291+
BlockDefs = otherWorld.BlockDefs;
292+
if (BlockDefs != BlockDefinition.GlobalDefs) BlockDefinition.Save(false, this);
293+
foreach (Player p in Players) {
294+
BlockDefinition.SendNowBlocks(p);
295+
}
296+
WorldManager.SaveWorldList();
297+
}
298+
285299

286300
/// <summary> Controls if the map should be loaded before players enter.
287301
/// Map is immediately loaded when Preload is set to true.

fCraft/World/WorldManager.cs

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -733,29 +733,77 @@ public static World AddWorld( [CanBeNull] Player player, [NotNull] string name,
733733
if( RaiseWorldCreatingEvent( player, name, map ) ) {
734734
throw new WorldOpException( name, WorldOpExceptionCode.Cancelled );
735735
}
736+
newWorld = new World(name) { Map = map };
736737

737-
newWorld = new World( name ) { Map = map };
738-
if( preload ) newWorld.Preload = true;
739-
if( map != null ) newWorld.SaveMap();
740-
738+
if ( preload ) newWorld.Preload = true;
739+
ParseCPEMetaData(ref newWorld, map, name);
740+
if (map != null) newWorld.SaveMap();
741741
WorldIndex.Add( name.ToLower(), newWorld );
742742
UpdateWorldList();
743743

744744
RaiseWorldCreatedEvent( player, newWorld );
745745
}
746-
747-
// Load CustomBlock settings
748-
string blockDefPath = Path.Combine(Paths.BlockDefsDirectory, name + ".txt");
746+
return newWorld;
747+
}
748+
749+
/// <summary> Parses the CPE data of a world when adding or replacing a world in the list. </summary>
750+
/// <param name="world"> World being added/updated. </param>
751+
/// <param name="map"> Map to assign to the newly created world. </param>
752+
/// <returns> World with updated CPE data if there is any. </returns>
753+
public static void ParseCPEMetaData(ref World world, Map map, string previousMapName) {
754+
if (map != null) {
755+
if (map.Metadata.ContainsKey("CPE", "HasEnvColors")) {
756+
world.SkyColor = map.Metadata.Get("CPE", "SkyColor").Value ?? "normal";
757+
world.CloudColor = map.Metadata.Get("CPE", "CloudColor").Value ?? "normal";
758+
world.FogColor = map.Metadata.Get("CPE", "FogColor").Value ?? "normal";
759+
world.LightColor = map.Metadata.Get("CPE", "LightColor").Value ?? "normal";
760+
world.ShadowColor = map.Metadata.Get("CPE", "ShadowColor").Value ?? "normal";
761+
map.Metadata.Remove("CPE", "SkyColor");
762+
map.Metadata.Remove("CPE", "CloudColor");
763+
map.Metadata.Remove("CPE", "FogColor");
764+
map.Metadata.Remove("CPE", "LightColor");
765+
map.Metadata.Remove("CPE", "ShadowColor");
766+
map.Metadata.Remove("CPE", "HasEnvColors");
767+
}
768+
if (map.Metadata.ContainsKey("CPE", "HasEnvMapAppearance")) {
769+
world.HorizonBlock = byte.Parse(map.Metadata.Get("CPE", "HorizonBlock").Value);
770+
world.EdgeBlock = byte.Parse(map.Metadata.Get("CPE", "EdgeBlock").Value);
771+
world.EdgeLevel = short.Parse(map.Metadata.Get("CPE", "EdgeLevel").Value);
772+
if (map.Metadata.ContainsKey("CPE", "Texture")) {
773+
string url = map.Metadata.Get("CPE", "Texture").Value;
774+
world.Texture = url == Server.DefaultTerrain ? "default" : url;
775+
map.Metadata.Remove("CPE", "Texture");
776+
}
777+
map.Metadata.Remove("CPE", "HorizonBlock");
778+
map.Metadata.Remove("CPE", "EdgeBlock");
779+
map.Metadata.Remove("CPE", "EdgeLevel");
780+
map.Metadata.Remove("CPE", "HasEnvMapAppearance");
781+
}
782+
if (map.Metadata.ContainsKey("CPE", "Weather")) {
783+
world.Weather = byte.Parse(map.Metadata.Get("CPE", "Weather").Value);
784+
map.Metadata.Remove("CPE", "Weather");
785+
}
786+
if (map.Metadata.ContainsKey("CPE", "ClickDistance")) {
787+
world.MaxReach = short.Parse(map.Metadata.Get("CPE", "ClickDistance").Value);
788+
map.Metadata.Remove("CPE", "ClickDistance");
789+
}
790+
}
791+
string blockDefPath = Path.Combine(Paths.BlockDefsDirectory, previousMapName + ".txt");
792+
if (map != null && map.Metadata.ContainsKey("CPE", "HasBlockDefFile")) {
793+
string blockDefFileName = map.Metadata.Get("CPE", "BlockDefFileName").Value;
794+
blockDefPath = Path.Combine(Paths.BlockDefsDirectory, blockDefFileName + ".txt");
795+
if (blockDefFileName != previousMapName) {
796+
map.Metadata["CPE", "BlockDefFileName"] = world.Name;
797+
}
798+
}
749799
if (File.Exists(blockDefPath)) {
750-
int count;
751-
BlockDefinition[] defs = BlockDefinition.Load(blockDefPath, out count);
800+
BlockDefinition[] defs = BlockDefinition.Load(blockDefPath, out _);
752801
for (int i = 0; i < defs.Length; i++) {
753802
if (defs[i] == null) defs[i] = BlockDefinition.GlobalDefs[i];
754803
}
755-
newWorld.BlockDefs = defs;
804+
world.BlockDefs = defs;
805+
BlockDefinition.Save(false, world);
756806
}
757-
758-
return newWorld;
759807
}
760808

761809

0 commit comments

Comments
 (0)