Move JSON parsing into core

This commit is contained in:
UnknownShadow200 2018-07-19 17:25:18 +10:00
parent 9f0d908559
commit 7ff12b4026
18 changed files with 548 additions and 254 deletions

View File

@ -18,37 +18,54 @@
using System; using System;
using System.IO; using System.IO;
using MCGalaxy.Blocks; using MCGalaxy.Blocks;
using MCGalaxy.Config;
using MCGalaxy.Network; using MCGalaxy.Network;
using Newtonsoft.Json; using BlockID = System.UInt16;
using BlockID_ = System.UInt16;
using BlockRaw = System.Byte;
namespace MCGalaxy { namespace MCGalaxy {
public sealed class BlockDefinition { public sealed class BlockDefinition {
public ushort BlockID; // really raw block ID [ConfigUShort("BlockID", null)]
public string Name; public ushort RawID;
public byte CollideType; [ConfigString] public string Name;
public float Speed; [ConfigReal] public float Speed;
public ushort TopTex, SideTex, BottomTex; [ConfigByte] public byte CollideType;
public bool BlocksLight; [ConfigUShort] public ushort TopTex;
public byte WalkSound; [ConfigUShort] ushort SideTex;
public bool FullBright; [ConfigUShort] public ushort BottomTex;
public byte Shape;
public byte BlockDraw;
public byte FogDensity, FogR, FogG, FogB;
public byte FallBack;
// BlockDefinitionsExt fields
public byte MinX, MinY, MinZ;
public byte MaxX, MaxY, MaxZ;
// BlockDefinitionsExt version 2 fields
public bool Version2;
public ushort LeftTex, RightTex, FrontTex, BackTex;
[ConfigBool] public bool BlocksLight;
[ConfigByte] public byte WalkSound;
[ConfigBool] public bool FullBright;
[ConfigByte] public byte Shape;
[ConfigByte] public byte BlockDraw;
[ConfigByte] public byte FallBack;
[ConfigByte] public byte FogDensity;
[ConfigByte] public byte FogR;
[ConfigByte] public byte FogG;
[ConfigByte] public byte FogB;
// BlockDefinitionsExt fields
[ConfigByte] public byte MinX;
[ConfigByte] public byte MinY;
[ConfigByte] public byte MinZ;
[ConfigByte] public byte MaxX;
[ConfigByte] public byte MaxY;
[ConfigByte] public byte MaxZ;
// BlockDefinitionsExt version 2 fields
[ConfigBool] bool Version2;
[ConfigUShort] public ushort LeftTex;
[ConfigUShort] public ushort RightTex;
[ConfigUShort] public ushort FrontTex;
[ConfigUShort] public ushort BackTex;
[ConfigInt(null, null, -1, -1)]
public int InventoryOrder = -1; public int InventoryOrder = -1;
public BlockID_ GetBlock() { return Block.FromRaw(BlockID); } public BlockID GetBlock() { return Block.FromRaw(RawID); }
public void SetBlock(BlockID_ b) { BlockID = Block.ToRaw(b); } public void SetBlock(BlockID b) { RawID = Block.ToRaw(b); }
public const string GlobalPath = "blockdefs/global.json", GlobalBackupPath = "blockdefs/global.json.bak"; public const string GlobalPath = "blockdefs/global.json", GlobalBackupPath = "blockdefs/global.json.bak";
@ -56,80 +73,84 @@ namespace MCGalaxy {
public BlockDefinition Copy() { public BlockDefinition Copy() {
BlockDefinition def = new BlockDefinition(); BlockDefinition def = new BlockDefinition();
def.BlockID = BlockID; def.Name = Name; def.RawID = RawID; def.Name = Name;
def.CollideType = CollideType; def.Speed = Speed; def.CollideType = CollideType; def.Speed = Speed;
def.TopTex = TopTex; def.SideTex = SideTex; def.TopTex = TopTex; def.BottomTex = BottomTex;
def.BottomTex = BottomTex; def.BlocksLight = BlocksLight; def.BlocksLight = BlocksLight; def.WalkSound = WalkSound;
def.WalkSound = WalkSound; def.FullBright = FullBright; def.FullBright = FullBright; def.Shape = Shape;
def.Shape = Shape; def.BlockDraw = BlockDraw; def.BlockDraw = BlockDraw; def.FogDensity = FogDensity;
def.FogDensity = FogDensity; def.FogR = FogR; def.FogR = FogR; def.FogG = FogG; def.FogB = FogB;
def.FogG = FogG; def.FogB = FogB;
def.FallBack = FallBack; def.FallBack = FallBack;
def.MinX = MinX; def.MinY = MinY; def.MinZ = MinZ; def.MinX = MinX; def.MinY = MinY; def.MinZ = MinZ;
def.MaxX = MaxX; def.MaxY = MaxY; def.MaxZ = MaxZ; def.MaxX = MaxX; def.MaxY = MaxY; def.MaxZ = MaxZ;
def.Version2 = Version2;
def.LeftTex = LeftTex; def.RightTex = RightTex; def.LeftTex = LeftTex; def.RightTex = RightTex;
def.FrontTex = FrontTex; def.BackTex = BackTex; def.FrontTex = FrontTex; def.BackTex = BackTex;
def.InventoryOrder = InventoryOrder; def.InventoryOrder = InventoryOrder;
return def; return def;
} }
static ConfigElement[] elems;
public static BlockDefinition[] Load(bool global, string mapName) { public static BlockDefinition[] Load(bool global, string mapName) {
BlockDefinition[] defs = null; BlockDefinition[] defs = new BlockDefinition[Block.ExtendedCount];
string path = global ? GlobalPath : "blockdefs/lvl_" + mapName + ".json"; string path = global ? GlobalPath : "blockdefs/lvl_" + mapName + ".json";
if (!File.Exists(path)) return defs;
if (elems == null) elems = ConfigElement.GetAll(typeof(BlockDefinition));
try { try {
if (File.Exists(path)) { JsonContext ctx = new JsonContext();
string json = File.ReadAllText(path); ctx.Val = File.ReadAllText(path);
defs = JsonConvert.DeserializeObject<BlockDefinition[]>(json); JsonArray array = (JsonArray)Json.ParseStream(ctx);
} if (array == null) return defs;
} catch (Exception ex) {
Logger.LogError("Error Loading block defs from " + path, ex);
}
if (defs == null) return new BlockDefinition[Block.ExtendedCount];
for (int i = 0; i < defs.Length; i++) {
if (defs[i] != null && defs[i].Name == null) defs[i] = null;
BlockDefinition def = defs[i];
if (def == null) continue;
if (!def.Version2) { foreach (object raw in array) {
def.Version2 = true; JsonObject obj = (JsonObject)raw;
def.SetSideTex(def.SideTex); if (obj == null) continue;
BlockDefinition def = new BlockDefinition();
obj.Deserialise(elems, def, "Block definition");
if (String.IsNullOrEmpty(def.Name)) continue;
BlockID block = def.GetBlock();
if (block >= defs.Length) {
Logger.Log(LogType.Warning, "Invalid block ID: " + def.RawID);
} else {
defs[block] = def;
}
} }
} catch (Exception ex) {
Logger.LogError("Error Loading block defs from " + path, ex);
} }
return defs;
// Need to adjust index of raw block ID
BlockDefinition[] adjDefs = new BlockDefinition[Block.ExtendedCount];
for (int b = 0; b < defs.Length; b++) {
BlockDefinition def = defs[b];
if (def == null) continue;
BlockID_ block = def.GetBlock();
if (block >= adjDefs.Length) {
Logger.Log(LogType.Warning, "Invalid block ID: " + block);
} else {
adjDefs[block] = def;
}
}
return adjDefs;
} }
public static void Save(bool global, Level lvl) { public static void Save(bool global, Level lvl) {
BlockDefinition[] defs = global ? GlobalDefs : lvl.CustomBlockDefs; if (elems == null) elems = ConfigElement.GetAll(typeof(BlockDefinition));
// We don't want to save global blocks in the level's custom blocks list
if (!global) {
BlockDefinition[] realDefs = new BlockDefinition[defs.Length];
for (int i = 0; i < defs.Length; i++) {
realDefs[i] = defs[i] == GlobalDefs[i] ? null : defs[i];
}
defs = realDefs;
}
string json = JsonConvert.SerializeObject(defs, Formatting.Indented);
string path = global ? GlobalPath : "blockdefs/lvl_" + lvl.MapName + ".json"; string path = global ? GlobalPath : "blockdefs/lvl_" + lvl.MapName + ".json";
File.WriteAllText(path, json); BlockDefinition[] defs = global ? GlobalDefs : lvl.CustomBlockDefs;
using (StreamWriter w = new StreamWriter(path)) {
w.WriteLine("[");
SaveEntries(w, global, defs);
w.WriteLine();
w.WriteLine("]");
}
}
static void SaveEntries(StreamWriter w, bool global, BlockDefinition[] defs) {
bool first = true;
for (int i = 0; i < defs.Length; i++) {
BlockDefinition def = defs[i];
// don't want to save global blocks in the level's custom blocks list
if (!global && def == GlobalDefs[i]) def = null;
if (def == null) continue;
def.SideTex = def.RightTex; def.Version2 = true;
// need to add ',' from last element
if (!first) w.WriteLine(", ");
Json.Serialise(w, elems, def);
first = false;
}
} }
public static void LoadGlobal() { public static void LoadGlobal() {
@ -141,8 +162,8 @@ namespace MCGalaxy {
if (File.Exists(GlobalPath)) { if (File.Exists(GlobalPath)) {
File.Copy(GlobalPath, GlobalBackupPath, true); File.Copy(GlobalPath, GlobalBackupPath, true);
} }
} catch (Exception ex) { } catch (Exception ex) {
Logger.LogError("Error backing up global block defs", ex); Logger.LogError("Error backing up global block defs", ex);
} }
// As the BlockDefinition instances in levels will now be different // As the BlockDefinition instances in levels will now be different
@ -160,14 +181,14 @@ namespace MCGalaxy {
if ((lvl.Props[b].ChangedScope & 2) == 0) { if ((lvl.Props[b].ChangedScope & 2) == 0) {
lvl.Props[b] = Block.Props[b]; lvl.Props[b] = Block.Props[b];
} }
lvl.UpdateCustomBlock((BlockID_)b, GlobalDefs[b]); lvl.UpdateCustomBlock((BlockID)b, GlobalDefs[b]);
} }
} }
} }
public static void Add(BlockDefinition def, BlockDefinition[] defs, Level level) { public static void Add(BlockDefinition def, BlockDefinition[] defs, Level level) {
BlockID_ block = def.GetBlock(); BlockID block = def.GetBlock();
bool global = defs == GlobalDefs; bool global = defs == GlobalDefs;
if (global) UpdateGlobalCustom(block, def); if (global) UpdateGlobalCustom(block, def);
@ -178,7 +199,7 @@ namespace MCGalaxy {
Player[] players = PlayerInfo.Online.Items; Player[] players = PlayerInfo.Online.Items;
foreach (Player pl in players) { foreach (Player pl in players) {
if (!global && pl.level != level) continue; if (!global && pl.level != level) continue;
if (!pl.hasBlockDefs || def.BlockID > pl.MaxRawBlock) continue; if (!pl.hasBlockDefs || def.RawID > pl.MaxRawBlock) continue;
if (global && pl.level.CustomBlockDefs[block] != GlobalDefs[block]) continue; if (global && pl.level.CustomBlockDefs[block] != GlobalDefs[block]) continue;
pl.Send(def.MakeDefinePacket(pl)); pl.Send(def.MakeDefinePacket(pl));
@ -188,7 +209,7 @@ namespace MCGalaxy {
} }
public static void Remove(BlockDefinition def, BlockDefinition[] defs, Level level) { public static void Remove(BlockDefinition def, BlockDefinition[] defs, Level level) {
BlockID_ block = def.GetBlock(); BlockID block = def.GetBlock();
bool global = defs == GlobalDefs; bool global = defs == GlobalDefs;
if (global) UpdateGlobalCustom(block, null); if (global) UpdateGlobalCustom(block, null);
@ -199,7 +220,7 @@ namespace MCGalaxy {
Player[] players = PlayerInfo.Online.Items; Player[] players = PlayerInfo.Online.Items;
foreach (Player pl in players) { foreach (Player pl in players) {
if (!global && pl.level != level) continue; if (!global && pl.level != level) continue;
if (!pl.hasBlockDefs || def.BlockID > pl.MaxRawBlock) continue; if (!pl.hasBlockDefs || def.RawID > pl.MaxRawBlock) continue;
if (global && pl.level.CustomBlockDefs[block] != null) continue; if (global && pl.level.CustomBlockDefs[block] != null) continue;
pl.Send(Packet.UndefineBlock(def, pl.hasExtBlocks)); pl.Send(Packet.UndefineBlock(def, pl.hasExtBlocks));
@ -211,12 +232,12 @@ namespace MCGalaxy {
Player[] players = PlayerInfo.Online.Items; Player[] players = PlayerInfo.Online.Items;
foreach (Player pl in players) { foreach (Player pl in players) {
if (!global && pl.level != level) continue; if (!global && pl.level != level) continue;
if (!pl.Supports(CpeExt.InventoryOrder) || def.BlockID > pl.MaxRawBlock) continue; if (!pl.Supports(CpeExt.InventoryOrder) || def.RawID > pl.MaxRawBlock) continue;
SendLevelInventoryOrder(pl); SendLevelInventoryOrder(pl);
} }
} }
static void UpdateGlobalCustom(BlockID_ block, BlockDefinition def) { static void UpdateGlobalCustom(BlockID block, BlockDefinition def) {
Level[] loaded = LevelInfo.Loaded.Items; Level[] loaded = LevelInfo.Loaded.Items;
foreach (Level lvl in loaded) { foreach (Level lvl in loaded) {
if (lvl.CustomBlockDefs[block] != GlobalDefs[block]) continue; if (lvl.CustomBlockDefs[block] != GlobalDefs[block]) continue;
@ -225,12 +246,11 @@ namespace MCGalaxy {
} }
public void SetAllTex(ushort id) { public void SetAllTex(ushort id) {
SetSideTex(id); SetSideTex(id);
TopTex = id; BottomTex = id; TopTex = id; BottomTex = id;
} }
public void SetSideTex(ushort id) { public void SetSideTex(ushort id) {
SideTex = id;
LeftTex = id; RightTex = id; FrontTex = id; BackTex = id; LeftTex = id; RightTex = id; FrontTex = id; BackTex = id;
} }
@ -239,7 +259,7 @@ namespace MCGalaxy {
BlockDefinition[] defs = pl.level.CustomBlockDefs; BlockDefinition[] defs = pl.level.CustomBlockDefs;
for (int i = 0; i < defs.Length; i++) { for (int i = 0; i < defs.Length; i++) {
BlockDefinition def = defs[i]; BlockDefinition def = defs[i];
if (def == null || def.BlockID > pl.MaxRawBlock) continue; if (def == null || def.RawID > pl.MaxRawBlock) continue;
pl.Send(def.MakeDefinePacket(pl)); pl.Send(def.MakeDefinePacket(pl));
} }
} }
@ -258,23 +278,23 @@ namespace MCGalaxy {
// Fill slots with explicit order // Fill slots with explicit order
for (int i = 0; i < defs.Length; i++) { for (int i = 0; i < defs.Length; i++) {
BlockDefinition def = defs[i]; BlockDefinition def = defs[i];
if (def == null || def.BlockID > pl.MaxRawBlock) continue; if (def == null || def.RawID > pl.MaxRawBlock) continue;
if (def.InventoryOrder == -1) continue; if (def.InventoryOrder == -1) continue;
if (def.InventoryOrder != 0) { if (def.InventoryOrder != 0) {
if (order_to_blocks[def.InventoryOrder] != -1) continue; if (order_to_blocks[def.InventoryOrder] != -1) continue;
order_to_blocks[def.InventoryOrder] = def.BlockID; order_to_blocks[def.InventoryOrder] = def.RawID;
} }
block_to_orders[def.BlockID] = def.InventoryOrder; block_to_orders[def.RawID] = def.InventoryOrder;
} }
// Put blocks into their default slot if slot is unused // Put blocks into their default slot if slot is unused
for (int i = 0; i < defs.Length; i++) { for (int i = 0; i < defs.Length; i++) {
BlockDefinition def = defs[i]; BlockDefinition def = defs[i];
int raw = def != null ? def.BlockID : i; int raw = def != null ? def.RawID : i;
if (raw > pl.MaxRawBlock || (def == null && raw >= Block.CpeCount)) continue; if (raw > pl.MaxRawBlock || (def == null && raw >= Block.CpeCount)) continue;
if (def != null && def.InventoryOrder >= 0) continue; if (def != null && def.InventoryOrder >= 0) continue;
if (order_to_blocks[raw] == -1) { if (order_to_blocks[raw] == -1) {
order_to_blocks[raw] = raw; order_to_blocks[raw] = raw;
block_to_orders[raw] = raw; block_to_orders[raw] = raw;
@ -284,7 +304,7 @@ namespace MCGalaxy {
// Push blocks whose slots conflict with other blocks into free slots at end // Push blocks whose slots conflict with other blocks into free slots at end
for (int i = defs.Length - 1; i >= 0; i--) { for (int i = defs.Length - 1; i >= 0; i--) {
BlockDefinition def = defs[i]; BlockDefinition def = defs[i];
int raw = def != null ? def.BlockID : i; int raw = def != null ? def.RawID : i;
if (raw > pl.MaxRawBlock || (def == null && raw >= Block.CpeCount)) continue; if (raw > pl.MaxRawBlock || (def == null && raw >= Block.CpeCount)) continue;
if (block_to_orders[raw] != -1) continue; if (block_to_orders[raw] != -1) continue;
@ -301,12 +321,12 @@ namespace MCGalaxy {
int order = block_to_orders[raw]; int order = block_to_orders[raw];
if (order == -1) order = 0; if (order == -1) order = 0;
BlockDefinition def = defs[Block.FromRaw((BlockID_)raw)]; BlockDefinition def = defs[Block.FromRaw((BlockID)raw)];
if (def == null && raw >= Block.CpeCount) continue; if (def == null && raw >= Block.CpeCount) continue;
// Special case, don't want 255 getting hidden by default // Special case, don't want 255 getting hidden by default
if (raw == 255 && def.InventoryOrder == -1) continue; if (raw == 255 && def.InventoryOrder == -1) continue;
pl.Send(Packet.SetInventoryOrder((BlockID_)raw, (BlockID_)order, pl.hasExtBlocks)); pl.Send(Packet.SetInventoryOrder((BlockID)raw, (BlockID)order, pl.hasExtBlocks));
} }
} }
@ -320,7 +340,7 @@ namespace MCGalaxy {
} }
} }
public static void UpdateFallback(bool global, BlockID_ block, Level level) { public static void UpdateFallback(bool global, BlockID block, Level level) {
Player[] players = PlayerInfo.Online.Items; Player[] players = PlayerInfo.Online.Items;
foreach (Player pl in players) { foreach (Player pl in players) {
if (!global && pl.level != level) continue; if (!global && pl.level != level) continue;

View File

@ -51,7 +51,6 @@ namespace MCGalaxy.Blocks {
def.FallBack = (BlockRaw)b; def.FallBack = (BlockRaw)b;
def.MaxX = 16; def.MaxZ = Height(b); def.MaxY = 16; def.MaxX = 16; def.MaxZ = Height(b); def.MaxY = 16;
def.Version2 = true;
return def; return def;
} }

View File

@ -16,67 +16,100 @@
permissions and limitations under the Licenses. permissions and limitations under the Licenses.
*/ */
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using MCGalaxy.Maths; using MCGalaxy.Config;
using Newtonsoft.Json;
namespace MCGalaxy.Bots { namespace MCGalaxy.Bots {
/// <summary> Maintains persistent data for in-game bots. </summary> /// <summary> Maintains persistent data for in-game bots. </summary>
public static class BotsFile { public static class BotsFile {
public static string BotsPath(string mapName) { public static string BotsPath(string map) { return "extra/bots/" + map + ".json"; }
return "extra/bots/" + mapName + ".json"; static ConfigElement[] elems;
}
public static void Load(Level lvl) { lock (lvl.botsIOLock) { LoadCore(lvl); } } public static void Load(Level lvl) { lock (lvl.botsIOLock) { LoadCore(lvl); } }
static void LoadCore(Level lvl) { static void LoadCore(Level lvl) {
string path = BotsPath(lvl.MapName); string path = BotsPath(lvl.MapName);
if (!File.Exists(path)) return; if (!File.Exists(path)) return;
string json = File.ReadAllText(path); string json = File.ReadAllText(path);
BotProperties[] bots = JsonConvert.DeserializeObject<BotProperties[]>(json); List<BotProperties> props = null;
foreach (BotProperties props in bots) { try {
if (String.IsNullOrEmpty(props.DisplayName)) { props = ReadAll(json);
props.DisplayName = props.Name; } catch (Exception ex) {
} Logger.LogError("Reading bots file", ex); return;
}
PlayerBot bot = new PlayerBot(props.Name, lvl);
props.ApplyTo(bot); foreach (BotProperties data in props) {
PlayerBot bot = new PlayerBot(data.Name, lvl);
data.ApplyTo(bot);
bot.SetModel(bot.Model, lvl); bot.SetModel(bot.Model, lvl);
LoadAi(props, bot); LoadAi(data, bot);
PlayerBot.Add(bot, false); PlayerBot.Add(bot, false);
} }
} }
internal static List<BotProperties> ReadAll(string json) {
List<BotProperties> props = new List<BotProperties>();
if (elems == null) elems = ConfigElement.GetAll(typeof(BotProperties));
JsonContext ctx = new JsonContext(); ctx.Val = json;
JsonArray array = (JsonArray)Json.ParseStream(ctx);
if (array == null) return props;
foreach (object raw in array) {
JsonObject obj = (JsonObject)raw;
if (obj == null) continue;
BotProperties data = new BotProperties();
obj.Deserialise(elems, data, "Bot properties");
if (String.IsNullOrEmpty(data.DisplayName)) data.DisplayName = data.Name;
props.Add(data);
}
return props;
}
public static void Save(Level lvl) { lock (lvl.botsIOLock) { SaveCore(lvl); } } public static void Save(Level lvl) { lock (lvl.botsIOLock) { SaveCore(lvl); } }
static void SaveCore(Level lvl) { static void SaveCore(Level lvl) {
PlayerBot[] bots = lvl.Bots.Items; PlayerBot[] bots = lvl.Bots.Items;
string path = BotsPath(lvl.MapName); string path = BotsPath(lvl.MapName);
if (!File.Exists(path) && bots.Length == 0) return; if (!File.Exists(path) && bots.Length == 0) return;
BotProperties[] props = new BotProperties[bots.Length]; List<BotProperties> props = new List<BotProperties>(bots.Length);
for (int i = 0; i < props.Length; i++) { for (int i = 0; i < bots.Length; i++) {
BotProperties savedProps = new BotProperties(); BotProperties data = new BotProperties();
savedProps.FromBot(bots[i]); data.FromBot(bots[i]);
props[i] = savedProps; props.Add(data);
} }
string json = JsonConvert.SerializeObject(props);
try { try {
File.WriteAllText(path, json); using (StreamWriter w = new StreamWriter(path)) { WriteAll(w, props); }
} catch (Exception ex) { } catch (Exception ex) {
Logger.LogError("Error saving bots to " + path, ex); Logger.LogError("Error saving bots to " + path, ex);
} }
}
internal static void WriteAll(StreamWriter w, List<BotProperties> props) {
w.WriteLine("[");
if (elems == null) elems = ConfigElement.GetAll(typeof(BotProperties));
for (int i = 0; i < props.Count; i++) {
Json.Serialise(w, elems, props[i]);
bool last = i == props.Count - 1;
w.WriteLine(last ? "" : ",");
}
w.WriteLine("]");
} }
internal static void LoadAi(BotProperties props, PlayerBot bot) { internal static void LoadAi(BotProperties props, PlayerBot bot) {
if (String.IsNullOrEmpty(props.AI)) return; if (String.IsNullOrEmpty(props.AI)) return;
try { try {
ScriptFile.Parse(Player.Console, bot, props.AI); ScriptFile.Parse(Player.Console, bot, props.AI);
} catch (Exception ex) { } catch (Exception ex) {
Logger.LogError("Error loading bot AI " + props.AI, ex); Logger.LogError("Error loading bot AI " + props.AI, ex);
} }
bot.cur = props.CurInstruction; bot.cur = props.CurInstruction;
@ -85,31 +118,32 @@ namespace MCGalaxy.Bots {
} }
public sealed class BotProperties { public sealed class BotProperties {
public string DisplayName { get; set; } [ConfigString] public string DisplayName;
public string Name { get; set; } [ConfigString] public string Name;
public string Level { get; set; } [ConfigString] public string Level;
public string Skin { get; set; } [ConfigString] public string Skin;
public string Model { get; set; } [ConfigString] public string Model;
public string Color { get; set; } [ConfigString] public string Color;
public string ClickedOnText { get; set; } [ConfigString] public string ClickedOnText;
public string DeathMessage { get; set; } [ConfigString] public string DeathMessage;
public string AI { get; set; } [ConfigString] public string AI;
public bool Kill { get; set; } [ConfigBool] public bool Kill;
public bool Hunt { get; set; } [ConfigBool] public bool Hunt;
public int CurInstruction { get; set; } [ConfigInt] public int CurInstruction;
public sbyte CurJump { get; set; } [ConfigInt] public int CurJump;
public int X { get; set; } [ConfigInt] public int X;
public int Y { get; set; } [ConfigInt] public int Y;
public int Z { get; set; } [ConfigInt] public int Z;
public byte RotX { get; set; } [ConfigByte] public byte RotX;
public byte RotY { get; set; } [ConfigByte] public byte RotY;
public byte BodyX { get; set; }
public byte BodyZ { get; set; } [ConfigByte] public byte BodyX;
public float ScaleX { get; set; } [ConfigByte] public byte BodyZ;
public float ScaleY { get; set; } [ConfigReal] public float ScaleX;
public float ScaleZ { get; set; } [ConfigReal] public float ScaleY;
[ConfigReal] public float ScaleZ;
public void FromBot(PlayerBot bot) { public void FromBot(PlayerBot bot) {
Name = bot.name; Level = bot.level.name; Name = bot.name; Level = bot.level.name;

View File

@ -44,7 +44,7 @@ namespace MCGalaxy {
public Position TargetPos; public Position TargetPos;
public bool movement = false; public bool movement = false;
public int movementSpeed = 3; public int movementSpeed = 3;
internal sbyte curJump = 0; internal int curJump = 0;
public PlayerBot(string n, Level lvl) { public PlayerBot(string n, Level lvl) {
name = n; DisplayName = n; SkinName = n; name = n; DisplayName = n; SkinName = n;

View File

@ -69,22 +69,22 @@ namespace MCGalaxy {
public static ColorDesc DefaultCol(char code) { public static ColorDesc DefaultCol(char code) {
switch (code) { switch (code) {
case '0': return new ColorDesc('0', "Black"); case '0': return new ColorDesc('0', "Black");
case '1': return new ColorDesc('1', "Navy"); case '1': return new ColorDesc('1', "Navy");
case '2': return new ColorDesc('2', "Green"); case '2': return new ColorDesc('2', "Green");
case '3': return new ColorDesc('3', "Teal"); case '3': return new ColorDesc('3', "Teal");
case '4': return new ColorDesc('4', "Maroon"); case '4': return new ColorDesc('4', "Maroon");
case '5': return new ColorDesc('5', "Purple"); case '5': return new ColorDesc('5', "Purple");
case '6': return new ColorDesc('6', "Gold"); case '6': return new ColorDesc('6', "Gold");
case '7': return new ColorDesc('7', "Silver"); case '7': return new ColorDesc('7', "Silver");
case '8': return new ColorDesc('8', "Gray"); case '8': return new ColorDesc('8', "Gray");
case '9': return new ColorDesc('9', "Blue"); case '9': return new ColorDesc('9', "Blue");
case 'a': return new ColorDesc('a', "Lime"); case 'a': return new ColorDesc('a', "Lime");
case 'b': return new ColorDesc('b', "Aqua"); case 'b': return new ColorDesc('b', "Aqua");
case 'c': return new ColorDesc('c', "Red"); case 'c': return new ColorDesc('c', "Red");
case 'd': return new ColorDesc('d', "Pink"); case 'd': return new ColorDesc('d', "Pink");
case 'e': return new ColorDesc('e', "Yellow"); case 'e': return new ColorDesc('e', "Yellow");
case 'f': return new ColorDesc('f', "White"); case 'f': return new ColorDesc('f', "White");
} }
ColorDesc col = default(ColorDesc); ColorDesc col = default(ColorDesc);
@ -120,21 +120,21 @@ namespace MCGalaxy {
if (code >= 'A' && code <= 'F') code += ' '; if (code >= 'A' && code <= 'F') code += ' ';
return IsDefined(code) ? Get(code).Name : ""; return IsDefined(code) ? Get(code).Name : "";
} }
static readonly string[] ircColors = new string[] { static readonly string[] ircColors = new string[] {
"\u000300", "\u000301", "\u000302", "\u000303", "\u000304", "\u000305", "\u000300", "\u000301", "\u000302", "\u000303", "\u000304", "\u000305",
"\u000306", "\u000307", "\u000308", "\u000309", "\u000310", "\u000311", "\u000306", "\u000307", "\u000308", "\u000309", "\u000310", "\u000311",
"\u000312", "\u000313", "\u000314", "\u000315", "\u000312", "\u000313", "\u000314", "\u000315",
}; };
static readonly string[] ircSingle = new string[] { static readonly string[] ircSingle = new string[] {
"\u00030", "\u00031", "\u00032", "\u00033", "\u00034", "\u00035", "\u00030", "\u00031", "\u00032", "\u00033", "\u00034", "\u00035",
"\u00036", "\u00037", "\u00038", "\u00039", "\u00036", "\u00037", "\u00038", "\u00039",
}; };
static readonly string[] ircReplacements = new string[] { static readonly string[] ircReplacements = new string[] {
white, black, navy, green, red, maroon, purple, gold, white, black, navy, green, red, maroon, purple, gold,
yellow, lime, teal, aqua, blue, pink, gray, silver, yellow, lime, teal, aqua, blue, pink, gray, silver,
}; };
static readonly Regex IrcTwoColorCode = new Regex("(\x03\\d{1,2}),\\d{1,2}"); static readonly Regex IrcTwoColorCode = new Regex("(\x03\\d{1,2}),\\d{1,2}");
public static string ConvertIRCToMC(string input) { public static string ConvertIRCToMC(string input) {
@ -187,7 +187,7 @@ namespace MCGalaxy {
if (col == 'I') { col = ServerConfig.IRCColor[1]; return true; } if (col == 'I') { col = ServerConfig.IRCColor[1]; return true; }
if (col == 'W') { col = ServerConfig.WarningErrorColor[1]; return true; } if (col == 'W') { col = ServerConfig.WarningErrorColor[1]; return true; }
return IsDefined(col); return IsDefined(col);
} }
/// <summary> Converts percentage color codes to their actual/real color codes. </summary> /// <summary> Converts percentage color codes to their actual/real color codes. </summary>
@ -234,8 +234,8 @@ namespace MCGalaxy {
char color = chars[i + 1]; char color = chars[i + 1];
if (!Map(ref color)) continue; if (!Map(ref color)) continue;
chars[i] = '&'; chars[i] = '&';
chars[i + 1] = color; chars[i + 1] = color;
i++; // skip over color code i++; // skip over color code
} }
} }
@ -254,8 +254,8 @@ namespace MCGalaxy {
} }
} }
return new string(output, 0, usedChars); return new string(output, 0, usedChars);
} }
/// <summary> Removes all non-existent color codes, and converts /// <summary> Removes all non-existent color codes, and converts
/// custom colors to their fallback standard color codes if required. </summary> /// custom colors to their fallback standard color codes if required. </summary>
public static string Cleanup(string value, bool supportsCustomCols) { public static string Cleanup(string value, bool supportsCustomCols) {
@ -322,38 +322,35 @@ namespace MCGalaxy {
if (!(hex.Length == 3 || hex.Length == 6)) return false; if (!(hex.Length == 3 || hex.Length == 6)) return false;
for (int i = 0; i < hex.Length; i++) { for (int i = 0; i < hex.Length; i++) {
if (Hex(hex[i]) == -1) return false; if (UnHex(hex[i]) == -1) return false;
} }
int R, G, B; int R, G, B;
if (hex.Length == 6) { if (hex.Length == 6) {
R = (Hex(hex[0]) << 4) | Hex(hex[1]); R = (UnHex(hex[0]) << 4) | UnHex(hex[1]);
G = (Hex(hex[2]) << 4) | Hex(hex[3]); G = (UnHex(hex[2]) << 4) | UnHex(hex[3]);
B = (Hex(hex[4]) << 4) | Hex(hex[5]); B = (UnHex(hex[4]) << 4) | UnHex(hex[5]);
} else { } else {
R = Hex(hex[0]); R |= (R << 4); R = UnHex(hex[0]); R |= (R << 4);
G = Hex(hex[1]); G |= (G << 4); G = UnHex(hex[1]); G |= (G << 4);
B = Hex(hex[2]); B |= (B << 4); B = UnHex(hex[2]); B |= (B << 4);
} }
c.R = (byte)R; c.G = (byte)G; c.B = (byte)B; c.A = 255; c.R = (byte)R; c.G = (byte)G; c.B = (byte)B; c.A = 255;
return true; return true;
} }
/// <summary> Parses an #RRGGBB hex color string. </summary> /// <summary> Parses an #RRGGBB hex color string. </summary>
public static ColorDesc ParseHex(string hex) { public static ColorDesc ParseHex(string hex) {
ColorDesc c; ColorDesc c;
if (!TryParseHex(hex, out c)) throw new ArgumentException("invalid input"); if (!TryParseHex(hex, out c)) throw new ArgumentException("invalid input");
return c; return c;
} }
static int Hex(char value) { public static int UnHex(char c) {
if (value >= '0' && value <= '9') if (c >= '0' && c <= '9') { return (int)(c - '0'); }
return (int)(value - '0'); if (c >= 'a' && c <= 'f') { return (int)(c - 'a') + 10; }
if (value >= 'a' && value <= 'f') if (c >= 'A' && c <= 'F') { return (int)(c - 'A') + 10; }
return (int)(value - 'a') + 10;
if (value >= 'A' && value <= 'F')
return (int)(value - 'A') + 10;
return -1; return -1;
} }
@ -386,7 +383,7 @@ namespace MCGalaxy {
r = (byte)(191 * ((hex >> 2) & 1) + 64 * (hex >> 3)); r = (byte)(191 * ((hex >> 2) & 1) + 64 * (hex >> 3));
g = (byte)(191 * ((hex >> 1) & 1) + 64 * (hex >> 3)); g = (byte)(191 * ((hex >> 1) & 1) + 64 * (hex >> 3));
b = (byte)(191 * ((hex >> 0) & 1) + 64 * (hex >> 3)); b = (byte)(191 * ((hex >> 0) & 1) + 64 * (hex >> 3));
} }
public bool IsModified() { public bool IsModified() {
if ((Code >= '0' && Code <= '9') || (Code >= 'a' && Code <= 'f')) { if ((Code >= '0' && Code <= '9') || (Code >= 'a' && Code <= 'f')) {

View File

@ -97,7 +97,6 @@ namespace MCGalaxy.Commands.CPE {
SetBD(p, global, new BlockDefinition()); SetBD(p, global, new BlockDefinition());
BlockDefinition def = GetBD(p, global); BlockDefinition def = GetBD(p, global);
def.Version2 = true;
def.SetBlock(target); def.SetBlock(target);
p.Message("Use %T{0} abort %Sat anytime to abort the creation process.", cmd); p.Message("Use %T{0} abort %Sat anytime to abort the creation process.", cmd);
@ -203,7 +202,7 @@ namespace MCGalaxy.Commands.CPE {
if (def.Shape == 0) { if (def.Shape == 0) {
p.Message(" Block is a sprite"); p.Message(" Block is a sprite");
p.Message(" Texture ID: {0}", def.SideTex); p.Message(" Texture ID: {0}", def.RightTex);
} else { } else {
p.Message(" Block is a cube from ({0}, {1}, {2}) to ({3}, {4}, {5})", p.Message(" Block is a cube from ({0}, {1}, {2}) to ({3}, {4}, {5})",
def.MinX, def.MinZ, def.MinY, def.MaxX, def.MaxZ, def.MaxY); def.MinX, def.MinZ, def.MinY, def.MaxX, def.MaxZ, def.MaxY);
@ -238,7 +237,7 @@ namespace MCGalaxy.Commands.CPE {
} }
static string FormatBlock(BlockDefinition def) { static string FormatBlock(BlockDefinition def) {
return "Custom block %T" + def.BlockID + " %Shas name %T" + def.Name; return "Custom block %T" + def.RawID + " %Shas name %T" + def.Name;
} }
static void RemoveHandler(Player p, string[] parts, bool global, string cmd) { static void RemoveHandler(Player p, string[] parts, bool global, string cmd) {
@ -254,7 +253,7 @@ namespace MCGalaxy.Commands.CPE {
BlockDefinition.Remove(def, defs, p.IsSuper ? null : p.level); BlockDefinition.Remove(def, defs, p.IsSuper ? null : p.level);
string scope = global ? "global" : "level"; string scope = global ? "global" : "level";
p.Message("Removed " + scope + " custom block " + def.Name + "(" + def.BlockID + ")"); p.Message("Removed " + scope + " custom block " + def.Name + "(" + def.RawID + ")");
BlockDefinition globalDef = BlockDefinition.GlobalDefs[block]; BlockDefinition globalDef = BlockDefinition.GlobalDefs[block];
if (!global && globalDef != null) if (!global && globalDef != null)
@ -291,8 +290,8 @@ namespace MCGalaxy.Commands.CPE {
if (bd.Shape == 0) bd.SetAllTex(bd.TopTex); if (bd.Shape == 0) bd.SetAllTex(bd.TopTex);
} }
} else if (step == 5) { } else if (step == 5) {
if (CommandParser.GetUShort(p, value, "Texture ID", ref bd.SideTex, 0, 255)) { if (CommandParser.GetUShort(p, value, "Texture ID", ref bd.RightTex, 0, 255)) {
bd.SetSideTex(bd.SideTex); bd.SetSideTex(bd.RightTex);
step++; step++;
} }
} else if (step == 6) { } else if (step == 6) {
@ -401,12 +400,12 @@ namespace MCGalaxy.Commands.CPE {
if (!EditUShort(p, value, "Top texture", ref def.TopTex, arg)) return; if (!EditUShort(p, value, "Top texture", ref def.TopTex, arg)) return;
break; break;
case "alltex": case "alltex":
if (!EditUShort(p, value, "All textures", ref def.SideTex, arg)) return; if (!EditUShort(p, value, "All textures", ref def.RightTex, arg)) return;
def.SetAllTex(def.SideTex); def.SetAllTex(def.RightTex);
break; break;
case "sidetex": case "sidetex":
if (!EditUShort(p, value, "Side texture", ref def.SideTex, arg)) return; if (!EditUShort(p, value, "Side texture", ref def.RightTex, arg)) return;
def.SetSideTex(def.SideTex); def.SetSideTex(def.RightTex);
break; break;
case "lefttex": case "lefttex":
if (!EditUShort(p, value, "Left texture", ref def.LeftTex, arg)) return; if (!EditUShort(p, value, "Left texture", ref def.LeftTex, arg)) return;
@ -481,7 +480,7 @@ namespace MCGalaxy.Commands.CPE {
} }
// Don't let multiple blocks be assigned to same order // Don't let multiple blocks be assigned to same order
if (order != def.BlockID && order != 0) { if (order != def.RawID && order != 0) {
for (int i = 0; i < defs.Length; i++) { for (int i = 0; i < defs.Length; i++) {
if (defs[i] == null || defs[i].InventoryOrder != order) continue; if (defs[i] == null || defs[i].InventoryOrder != order) continue;
p.Message("Block {0} already had order {1}", defs[i].Name, order); p.Message("Block {0} already had order {1}", defs[i].Name, order);
@ -489,11 +488,11 @@ namespace MCGalaxy.Commands.CPE {
} }
} }
def.InventoryOrder = order == def.BlockID ? -1 : order; def.InventoryOrder = order == def.RawID ? -1 : order;
BlockDefinition.UpdateOrder(def, global, level); BlockDefinition.UpdateOrder(def, global, level);
BlockDefinition.Save(global, level); BlockDefinition.Save(global, level);
p.Message("Set inventory order for {0} to {1}", blockName, p.Message("Set inventory order for {0} to {1}", blockName,
order == def.BlockID ? "default" : order.ToString()); order == def.RawID ? "default" : order.ToString());
return; return;
default: default:
p.Message("Unrecognised property: " + arg); return; p.Message("Unrecognised property: " + arg); return;
@ -528,7 +527,7 @@ namespace MCGalaxy.Commands.CPE {
} }
string scope = global ? "global" : "level"; string scope = global ? "global" : "level";
p.Message("Created a new " + scope + " custom block " + def.Name + "(" + def.BlockID + ")"); p.Message("Created a new " + scope + " custom block " + def.Name + "(" + def.RawID + ")");
block = def.GetBlock(); block = def.GetBlock();
BlockDefinition.Add(def, defs, p.IsSuper ? null : p.level); BlockDefinition.Add(def, defs, p.IsSuper ? null : p.level);

View File

@ -46,6 +46,8 @@ namespace MCGalaxy {
ConfigElement elem; ConfigElement elem;
elem.Field = field; elem.Field = field;
elem.Attrib = (ConfigAttribute)attributes[0]; elem.Attrib = (ConfigAttribute)attributes[0];
if (elem.Attrib.Name == null) elem.Attrib.Name = field.Name;
elems.Add(elem); elems.Add(elem);
} }
return elems.ToArray(); return elems.ToArray();

203
MCGalaxy/Config/JSON.cs Normal file
View File

@ -0,0 +1,203 @@
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace MCGalaxy.Config {
public class JsonContext {
public string Val; public int Idx; public bool Success = true;
public char Cur { get { return Val[Idx]; } }
internal StringBuilder strBuffer = new StringBuilder(96);
}
public sealed class JsonArray : List<object> { }
public sealed class JsonObject : Dictionary<string, object> {
public void Deserialise(ConfigElement[] elems, object instance, string group) {
foreach (KeyValuePair<string, object> e in this) {
string key = e.Key, value = (string)e.Value;
ConfigElement.Parse(elems, group, instance, key, value);
}
}
}
public static class Json {
const int T_NONE = 0, T_NUM = 1, T_TRUE = 2, T_FALSE = 3, T_NULL = 4;
static bool IsWhitespace(char c) {
return c == '\r' || c == '\n' || c == '\t' || c == ' ';
}
static bool NextConstant(JsonContext ctx, string value) {
if (ctx.Idx + value.Length > ctx.Val.Length) return false;
for (int i = 0; i < value.Length; i++) {
if (ctx.Val[ctx.Idx + i] != value[i]) return false;
}
ctx.Idx += value.Length; return true;
}
static int NextToken(JsonContext ctx) {
for (; ctx.Idx < ctx.Val.Length && IsWhitespace(ctx.Cur); ctx.Idx++);
if (ctx.Idx >= ctx.Val.Length) return T_NONE;
char c = ctx.Cur; ctx.Idx++;
if (c == '{' || c == '}') return c;
if (c == '[' || c == ']') return c;
if (c == ',' || c == '"' || c == ':') return c;
if (IsNumber(c)) return T_NUM;
ctx.Idx--;
if (NextConstant(ctx, "true")) return T_TRUE;
if (NextConstant(ctx, "false")) return T_FALSE;
if (NextConstant(ctx, "null")) return T_NULL;
// invalid token
ctx.Idx++; return T_NONE;
}
public static object ParseStream(JsonContext ctx) {
return ParseValue(NextToken(ctx), ctx);
}
static object ParseValue(int token, JsonContext ctx) {
switch (token) {
case '{': return ParseObject(ctx);
case '[': return ParseArray(ctx);
case '"': return ParseString(ctx);
case T_NUM: return ParseNumber(ctx);
case T_TRUE: return "true";
case T_FALSE: return "false";
case T_NULL: return null;
default: return null;
}
}
static JsonObject ParseObject(JsonContext ctx) {
JsonObject members = new JsonObject();
while (true) {
int token = NextToken(ctx);
if (token == ',') continue;
if (token == '}') return members;
if (token != '"') { ctx.Success = false; return null; }
string key = ParseString(ctx);
token = NextToken(ctx);
if (token != ':') { ctx.Success = false; return null; }
token = NextToken(ctx);
if (token == T_NONE) { ctx.Success = false; return null; }
members[key] = ParseValue(token, ctx);
}
}
static JsonArray ParseArray(JsonContext ctx) {
JsonArray elements = new JsonArray();
while (true) {
int token = NextToken(ctx);
if (token == ',') continue;
if (token == ']') return elements;
if (token == T_NONE) { ctx.Success = false; return null; }
elements.Add(ParseValue(token, ctx));
}
}
static string ParseString(JsonContext ctx) {
StringBuilder s = ctx.strBuffer; s.Length = 0;
for (; ctx.Idx < ctx.Val.Length;) {
char c = ctx.Cur; ctx.Idx++;
if (c == '"') return s.ToString();
if (c != '\\') { s.Append(c); continue; }
if (ctx.Idx >= ctx.Val.Length) break;
c = ctx.Cur; ctx.Idx++;
if (c == '/' || c == '\\' || c == '"') { s.Append(c); continue; }
if (c != 'u') break;
if (ctx.Idx + 4 > ctx.Val.Length) break;
// form of \uYYYY
int aH = Colors.UnHex(ctx.Val[ctx.Idx + 0]);
int aL = Colors.UnHex(ctx.Val[ctx.Idx + 1]);
int bH = Colors.UnHex(ctx.Val[ctx.Idx + 2]);
int bL = Colors.UnHex(ctx.Val[ctx.Idx + 3]);
if (aH == -1 || aL == -1 || bH == -1 || bL == -1) break;
int codePoint = (aH << 12) | (aL << 8) | (bH << 4) | bL;
s.Append((char)codePoint);
ctx.Idx += 4;
}
ctx.Success = false; return null;
}
static bool IsNumber(char c) {
return c == '-' || c == '.' || (c >= '0' && c <= '9');
}
static string ParseNumber(JsonContext ctx) {
int start = ctx.Idx - 1;
for (; ctx.Idx < ctx.Val.Length && IsNumber(ctx.Cur); ctx.Idx++);
return ctx.Val.Substring(start, ctx.Idx - start);
}
static char Hex(char c, int shift) {
int x = (c >> shift) & 0x0F;
return (char)(x <= 9 ? ('0' + x) : ('a' + (x - 10)));
}
static void WriteString(StreamWriter w, string value) {
w.Write('"');
foreach (char c in value) {
if (c == '/') { w.Write("\\/");
} else if (c == '\\') { w.Write("\\\\");
} else if (c == '"') { w.Write("\\\"");
} else if (c >= ' ' && c <= '~') { w.Write(c);
} else {
w.Write("\\u");
w.Write(Hex(c, 12)); w.Write(Hex(c, 8));
w.Write(Hex(c, 4)); w.Write(Hex(c, 0));
}
}
w.Write('"');
}
static void WriteValue(StreamWriter w, ConfigAttribute a, string value) {
if (String.IsNullOrEmpty(value)) {
w.Write("null");
} else if (a is ConfigBoolAttribute || a is ConfigIntAttribute || a is ConfigRealAttribute) {
w.Write(value);
} else {
WriteString(w, value);
}
}
public static void Serialise(StreamWriter w, ConfigElement[] elems, object instance) {
w.WriteLine("{");
for (int i = 0; i < elems.Length; i++) {
ConfigElement elem = elems[i];
ConfigAttribute a = elem.Attrib;
w.Write(" "); WriteString(w, a.Name); w.Write(": ");
object raw = elem.Field.GetValue(instance);
string value = elem.Attrib.Serialise(raw);
WriteValue(w, a, value);
bool last = i == elems.Length - 1;
w.WriteLine(last ? "" : ",");
}
w.Write('}');
}
}
}

View File

@ -23,6 +23,8 @@ namespace MCGalaxy.Config {
public class ConfigIntAttribute : ConfigAttribute { public class ConfigIntAttribute : ConfigAttribute {
int defValue, minValue, maxValue; int defValue, minValue, maxValue;
public ConfigIntAttribute()
: this(null, null, 0, int.MinValue, int.MaxValue) { }
public ConfigIntAttribute(string name, string section, int def, public ConfigIntAttribute(string name, string section, int def,
int min = int.MinValue, int max = int.MaxValue) int min = int.MinValue, int max = int.MaxValue)
: base(name, section) { defValue = def; minValue = min; maxValue = max; } : base(name, section) { defValue = def; minValue = min; maxValue = max; }
@ -47,8 +49,7 @@ namespace MCGalaxy.Config {
} }
// Hacky workaround for old ExponentialFog attribute // Hacky workaround for old ExponentialFog attribute
public sealed class ConfigBoolIntAttribute : ConfigIntAttribute { sealed class ConfigBoolIntAttribute : ConfigIntAttribute {
public ConfigBoolIntAttribute(string name, string section, int defValue) public ConfigBoolIntAttribute(string name, string section, int defValue)
: base(name, section, defValue, -1, 1) { : base(name, section, defValue, -1, 1) {
} }
@ -60,8 +61,7 @@ namespace MCGalaxy.Config {
} }
} }
public class ConfigBlockAttribute : ConfigIntAttribute { public class ConfigBlockAttribute : ConfigIntAttribute {
public ConfigBlockAttribute(string name, string section, int def) public ConfigBlockAttribute(string name, string section, int def)
: base(name, section, def, 0, Block.ExtendedCount - 1) { : base(name, section, def, 0, Block.ExtendedCount - 1) {
} }
@ -75,9 +75,35 @@ namespace MCGalaxy.Config {
} }
} }
public class ConfigByteAttribute : ConfigIntAttribute {
public ConfigByteAttribute() : this(null, null) { }
public ConfigByteAttribute(string name, string section)
: base(name, section, 0, 0, 255) { }
public override object Parse(string raw) {
int value = (int)base.Parse(raw);
// Can't directly unbox object to byte - must unbox to byte, then cast to byte
return (byte)value;
}
}
public class ConfigUShortAttribute : ConfigIntAttribute {
public ConfigUShortAttribute() : this(null, null) { }
public ConfigUShortAttribute(string name, string section)
: base(name, section, 0, 0, 65535) { }
public override object Parse(string raw) {
int value = (int)base.Parse(raw);
// Can't directly unbox object to ushort - must unbox to ushort, then cast to ushort
return (ushort)value;
}
}
public class ConfigRealAttribute : ConfigAttribute { public class ConfigRealAttribute : ConfigAttribute {
float defValue, minValue, maxValue; float defValue, minValue, maxValue;
public ConfigRealAttribute()
: this(null, null, 0, float.NegativeInfinity, float.PositiveInfinity) { }
public ConfigRealAttribute(string name, string section, float def, public ConfigRealAttribute(string name, string section, float def,
float min = float.NegativeInfinity, float max = float.PositiveInfinity) float min = float.NegativeInfinity, float max = float.PositiveInfinity)
: base(name, section) { defValue = def; minValue = min; maxValue = max; } : base(name, section) { defValue = def; minValue = min; maxValue = max; }

View File

@ -23,6 +23,7 @@ namespace MCGalaxy.Config {
public sealed class ConfigBoolAttribute : ConfigAttribute { public sealed class ConfigBoolAttribute : ConfigAttribute {
bool defValue; bool defValue;
public ConfigBoolAttribute() : this(null, null, false) { }
public ConfigBoolAttribute(string name, string section, bool def) public ConfigBoolAttribute(string name, string section, bool def)
: base(name, section) { defValue = def; } : base(name, section) { defValue = def; }
@ -34,6 +35,11 @@ namespace MCGalaxy.Config {
} }
return boolValue; return boolValue;
} }
public override string Serialise(object value) {
bool boolValue = (bool)value;
return boolValue ? "true" : "false";
}
} }
public sealed class ConfigPermAttribute : ConfigAttribute { public sealed class ConfigPermAttribute : ConfigAttribute {

View File

@ -56,8 +56,11 @@ namespace MCGalaxy.Config {
public ConfigStringAttribute(string name, string section, string def) public ConfigStringAttribute(string name, string section, string def)
: base(name, section) { defValue = def; } : base(name, section) { defValue = def; }
public ConfigStringAttribute()
: base(null, null) { allowEmpty = true; }
public override object Parse(string value) { public override object Parse(string value) {
if (value.Length == 0 && !allowEmpty) { if (String.IsNullOrEmpty(value) && !allowEmpty) {
Logger.Log(LogType.Warning, "Config key \"{0}\" has no value, using default of {1}", Name, defValue); Logger.Log(LogType.Warning, "Config key \"{0}\" has no value, using default of {1}", Name, defValue);
return defValue; return defValue;
} }

View File

@ -123,7 +123,7 @@ namespace MCGalaxy.Levels.IO {
NbtCompound props = (NbtCompound)tag; NbtCompound props = (NbtCompound)tag;
BlockDefinition def = new BlockDefinition(); BlockDefinition def = new BlockDefinition();
def.BlockID = props["ID"].ByteValue; def.RawID = props["ID"].ByteValue;
def.Name = props["Name"].StringValue; def.Name = props["Name"].StringValue;
def.CollideType = props["CollideType"].ByteValue; def.CollideType = props["CollideType"].ByteValue;
def.Speed = props["Speed"].FloatValue; def.Speed = props["Speed"].FloatValue;
@ -154,8 +154,6 @@ namespace MCGalaxy.Levels.IO {
BlockDefinition globalDef = BlockDefinition.GlobalDefs[block]; BlockDefinition globalDef = BlockDefinition.GlobalDefs[block];
if (PropsEquals(def, globalDef)) continue; if (PropsEquals(def, globalDef)) continue;
def.SideTex = def.LeftTex;
def.Version2 = true;
lvl.UpdateCustomBlock(block, def); lvl.UpdateCustomBlock(block, def);
hasBlockDefs = true; hasBlockDefs = true;
} }

View File

@ -71,9 +71,6 @@
<Reference Include="MySql.Data"> <Reference Include="MySql.Data">
<HintPath>..\MySql.Data.dll</HintPath> <HintPath>..\MySql.Data.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
@ -377,6 +374,7 @@
<Compile Include="Commands\World\CmdUnload.cs" /> <Compile Include="Commands\World\CmdUnload.cs" />
<Compile Include="Commands\World\PermissionCmds.cs" /> <Compile Include="Commands\World\PermissionCmds.cs" />
<Compile Include="Config\ConfigAttribute.cs" /> <Compile Include="Config\ConfigAttribute.cs" />
<Compile Include="Config\JSON.cs" />
<Compile Include="Config\NumberAttributes.cs" /> <Compile Include="Config\NumberAttributes.cs" />
<Compile Include="Config\OtherAttributes.cs" /> <Compile Include="Config\OtherAttributes.cs" />
<Compile Include="Config\ConfigElement.cs" /> <Compile Include="Config\ConfigElement.cs" />

View File

@ -16,11 +16,12 @@
permissions and limitations under the Licenses. permissions and limitations under the Licenses.
*/ */
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using MCGalaxy.Config;
using MCGalaxy.Events.ServerEvents; using MCGalaxy.Events.ServerEvents;
using Newtonsoft.Json;
namespace MCGalaxy.Network { namespace MCGalaxy.Network {
@ -62,7 +63,7 @@ namespace MCGalaxy.Network {
OnSendingHeartbeatEvent.Call(this, ref name); OnSendingHeartbeatEvent.Call(this, ref name);
name = Colors.Strip(name); name = Colors.Strip(name);
return return
"&port=" + ServerConfig.Port + "&port=" + ServerConfig.Port +
"&max=" + ServerConfig.MaxPlayers + "&max=" + ServerConfig.MaxPlayers +
"&name=" + Uri.EscapeDataString(name) + "&name=" + Uri.EscapeDataString(name) +
@ -76,43 +77,52 @@ namespace MCGalaxy.Network {
public override void OnRequest(HttpWebRequest request) { public override void OnRequest(HttpWebRequest request) {
if (proxyUrl == null) return; if (proxyUrl == null) return;
request.Proxy = new WebProxy(proxyUrl); request.Proxy = new WebProxy(proxyUrl);
} }
public override void OnResponse(string response) { public override void OnResponse(string response) {
if (String.IsNullOrEmpty(response)) return; if (String.IsNullOrEmpty(response)) return;
// in form of http://www.classicube.net/server/play/<hash>/ // in form of http://www.classicube.net/server/play/<hash>/
if (response.EndsWith("/")) if (response.EndsWith("/"))
response = response.Substring(0, response.Length - 1); response = response.Substring(0, response.Length - 1);
string hash = response.Substring(response.LastIndexOf('/') + 1); string hash = response.Substring(response.LastIndexOf('/') + 1);
// Run this code if we don't already have a hash or if the hash has changed // only need to do this when contents have changed
if (String.IsNullOrEmpty(Server.Hash) || hash != Server.Hash) { if (hash == Server.Hash) return;
Server.Hash = hash; Server.Hash = hash;
Server.URL = response; Server.URL = response;
if (!response.Contains("\"errors\": [")) {
Server.UpdateUrl(Server.URL);
File.WriteAllText("text/externalurl.txt", Server.URL);
Logger.Log(LogType.SystemActivity, "ClassiCube URL found: " + Server.URL);
} else {
string error = GetError(response);
if (error == null) error = "Error while finding URL. Is the port open?";
if (!response.Contains("\"errors\": [")) { Server.URL = error;
Server.UpdateUrl(Server.URL); Server.UpdateUrl(Server.URL);
File.WriteAllText("text/externalurl.txt", Server.URL); Logger.Log(LogType.Warning, response);
Logger.Log(LogType.SystemActivity, "ClassiCube URL found: " + Server.URL); }
} else { }
Response resp = JsonConvert.DeserializeObject<Response>(Server.URL);
if (resp.errors != null && resp.errors.Length > 0 && resp.errors[0].Length > 0) static string GetError(string json) {
Server.URL = resp.errors[0][0]; JsonContext ctx = new JsonContext(); ctx.Val = json;
else JsonObject obj = (JsonObject)Json.ParseStream(ctx);
Server.URL = "Error while finding URL. Is the port open?"; if (obj == null) return null;
Server.UpdateUrl(Server.URL);
Logger.Log(LogType.Warning, response); foreach (KeyValuePair<string, object> e in obj) {
if (!e.Key.CaselessEq("errors")) continue;
if (e.Value == null) return null;
// silly design, but form of json is: "errors": [ ["Error1"], ["Error2"] ]
JsonArray errors = (JsonArray)e.Value;
foreach (object raw in errors) {
JsonArray error = raw as JsonArray;
if (error != null && error.Count > 0) return (string)error[0];
} }
} }
} return null;
#pragma warning disable 0649
class Response {
public string[][] errors;
public string response;
public string status;
} }
#pragma warning restore 0649
} }
} }

View File

@ -385,7 +385,7 @@ namespace MCGalaxy.Network {
public static byte[] UndefineBlock(BlockDefinition def, bool extBlocks) { public static byte[] UndefineBlock(BlockDefinition def, bool extBlocks) {
byte[] buffer = new byte[extBlocks ? 3 : 2]; byte[] buffer = new byte[extBlocks ? 3 : 2];
buffer[0] = Opcode.CpeUndefineBlock; buffer[0] = Opcode.CpeUndefineBlock;
NetUtils.WriteBlock(def.BlockID, buffer, 1, extBlocks); NetUtils.WriteBlock(def.RawID, buffer, 1, extBlocks);
return buffer; return buffer;
} }
@ -414,7 +414,7 @@ namespace MCGalaxy.Network {
// speed = 2^((raw - 128) / 64); // speed = 2^((raw - 128) / 64);
// therefore raw = 64log2(speed) + 128 // therefore raw = 64log2(speed) + 128
byte rawSpeed = (byte)(64 * Math.Log(def.Speed, 2) + 128); byte rawSpeed = (byte)(64 * Math.Log(def.Speed, 2) + 128);
NetUtils.WriteBlock(def.BlockID, buffer, i, extBlocks); NetUtils.WriteBlock(def.RawID, buffer, i, extBlocks);
i += extBlocks ? 2 : 1; i += extBlocks ? 2 : 1;
NetUtils.Write(def.Name, buffer, i, hasCP437); NetUtils.Write(def.Name, buffer, i, hasCP437);
i += NetUtils.StringSize; i += NetUtils.StringSize;
@ -426,7 +426,7 @@ namespace MCGalaxy.Network {
WriteTex(buffer, ref i, def.LeftTex, extTexs); WriteTex(buffer, ref i, def.RightTex, extTexs); WriteTex(buffer, ref i, def.LeftTex, extTexs); WriteTex(buffer, ref i, def.RightTex, extTexs);
WriteTex(buffer, ref i, def.FrontTex, extTexs); WriteTex(buffer, ref i, def.BackTex, extTexs); WriteTex(buffer, ref i, def.FrontTex, extTexs); WriteTex(buffer, ref i, def.BackTex, extTexs);
} else { } else {
WriteTex(buffer, ref i, def.SideTex, extTexs); WriteTex(buffer, ref i, def.RightTex, extTexs);
} }
WriteTex(buffer, ref i, def.BottomTex, extTexs); WriteTex(buffer, ref i, def.BottomTex, extTexs);

View File

@ -236,7 +236,7 @@ namespace MCGalaxy {
BlockDefinition def = defs[i]; BlockDefinition def = defs[i];
if (def == BlockDefinition.GlobalDefs[i] || def == null) continue; if (def == BlockDefinition.GlobalDefs[i] || def == null) continue;
if (def.BlockID > MaxRawBlock) continue; if (def.RawID > MaxRawBlock) continue;
Send(Packet.UndefineBlock(def, hasExtBlocks)); Send(Packet.UndefineBlock(def, hasExtBlocks));
} }
} }

View File

@ -91,7 +91,6 @@ namespace MCGalaxy {
CheckFile("MySql.Data.dll"); CheckFile("MySql.Data.dll");
CheckFile("sqlite3_x32.dll"); CheckFile("sqlite3_x32.dll");
CheckFile("sqlite3_x64.dll"); CheckFile("sqlite3_x64.dll");
CheckFile("Newtonsoft.Json.dll");
CheckFile("LibNoise.dll"); CheckFile("LibNoise.dll");
EnsureFilesExist(); EnsureFilesExist();

View File

@ -20,9 +20,7 @@ using System.Collections.Generic;
using System.Data; using System.Data;
using System.IO; using System.IO;
using MCGalaxy.Bots; using MCGalaxy.Bots;
using MCGalaxy.DB;
using MCGalaxy.SQL; using MCGalaxy.SQL;
using Newtonsoft.Json;
namespace MCGalaxy.Tasks { namespace MCGalaxy.Tasks {
internal static class UpgradeTasks { internal static class UpgradeTasks {
@ -137,12 +135,12 @@ namespace MCGalaxy.Tasks {
Logger.Log(LogType.SystemActivity, "Making bots file per-level.. " + Logger.Log(LogType.SystemActivity, "Making bots file per-level.. " +
"saved backup of global bots file to extra/bots.json.bak"); "saved backup of global bots file to extra/bots.json.bak");
BotProperties[] bots = JsonConvert.DeserializeObject<BotProperties[]>(json); List<BotProperties> bots = BotsFile.ReadAll(json);
Dictionary<string, List<BotProperties>> botsByLevel = new Dictionary<string, List<BotProperties>>(); Dictionary<string, List<BotProperties>> botsByLevel = new Dictionary<string, List<BotProperties>>();
foreach (BotProperties bot in bots) { foreach (BotProperties bot in bots) {
List<BotProperties> levelBots; List<BotProperties> levelBots;
if (bot.Level == null || bot.Level.Length == 0) continue; if (String.IsNullOrEmpty(bot.Level)) continue;
if (!botsByLevel.TryGetValue(bot.Level, out levelBots)) { if (!botsByLevel.TryGetValue(bot.Level, out levelBots)) {
levelBots = new List<BotProperties>(); levelBots = new List<BotProperties>();
@ -152,8 +150,10 @@ namespace MCGalaxy.Tasks {
} }
foreach (var kvp in botsByLevel) { foreach (var kvp in botsByLevel) {
json = JsonConvert.SerializeObject(kvp.Value); string path = BotsFile.BotsPath(kvp.Key);
File.WriteAllText(BotsFile.BotsPath(kvp.Key), json); using (StreamWriter w = new StreamWriter(path)) {
BotsFile.WriteAll(w, kvp.Value);
}
} }
if (Server.mainLevel.Bots.Count == 0) { if (Server.mainLevel.Bots.Count == 0) {