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.IO;
using MCGalaxy.Blocks;
using MCGalaxy.Config;
using MCGalaxy.Network;
using Newtonsoft.Json;
using BlockID_ = System.UInt16;
using BlockRaw = System.Byte;
using BlockID = System.UInt16;
namespace MCGalaxy {
public sealed class BlockDefinition {
public ushort BlockID; // really raw block ID
public string Name;
public byte CollideType;
public float Speed;
public ushort TopTex, SideTex, BottomTex;
public bool BlocksLight;
public byte WalkSound;
public bool FullBright;
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;
[ConfigUShort("BlockID", null)]
public ushort RawID;
[ConfigString] public string Name;
[ConfigReal] public float Speed;
[ConfigByte] public byte CollideType;
[ConfigUShort] public ushort TopTex;
[ConfigUShort] ushort SideTex;
[ConfigUShort] public ushort BottomTex;
[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 BlockID_ GetBlock() { return Block.FromRaw(BlockID); }
public void SetBlock(BlockID_ b) { BlockID = Block.ToRaw(b); }
public BlockID GetBlock() { return Block.FromRaw(RawID); }
public void SetBlock(BlockID b) { RawID = Block.ToRaw(b); }
public const string GlobalPath = "blockdefs/global.json", GlobalBackupPath = "blockdefs/global.json.bak";
@ -56,80 +73,84 @@ namespace MCGalaxy {
public BlockDefinition Copy() {
BlockDefinition def = new BlockDefinition();
def.BlockID = BlockID; def.Name = Name;
def.RawID = RawID; def.Name = Name;
def.CollideType = CollideType; def.Speed = Speed;
def.TopTex = TopTex; def.SideTex = SideTex;
def.BottomTex = BottomTex; def.BlocksLight = BlocksLight;
def.WalkSound = WalkSound; def.FullBright = FullBright;
def.Shape = Shape; def.BlockDraw = BlockDraw;
def.FogDensity = FogDensity; def.FogR = FogR;
def.FogG = FogG; def.FogB = FogB;
def.TopTex = TopTex; def.BottomTex = BottomTex;
def.BlocksLight = BlocksLight; def.WalkSound = WalkSound;
def.FullBright = FullBright; def.Shape = Shape;
def.BlockDraw = BlockDraw; def.FogDensity = FogDensity;
def.FogR = FogR; def.FogG = FogG; def.FogB = FogB;
def.FallBack = FallBack;
def.MinX = MinX; def.MinY = MinY; def.MinZ = MinZ;
def.MaxX = MaxX; def.MaxY = MaxY; def.MaxZ = MaxZ;
def.Version2 = Version2;
def.LeftTex = LeftTex; def.RightTex = RightTex;
def.FrontTex = FrontTex; def.BackTex = BackTex;
def.InventoryOrder = InventoryOrder;
return def;
}
static ConfigElement[] elems;
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";
if (!File.Exists(path)) return defs;
if (elems == null) elems = ConfigElement.GetAll(typeof(BlockDefinition));
try {
if (File.Exists(path)) {
string json = File.ReadAllText(path);
defs = JsonConvert.DeserializeObject<BlockDefinition[]>(json);
}
} 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;
JsonContext ctx = new JsonContext();
ctx.Val = File.ReadAllText(path);
JsonArray array = (JsonArray)Json.ParseStream(ctx);
if (array == null) return defs;
if (!def.Version2) {
def.Version2 = true;
def.SetSideTex(def.SideTex);
foreach (object raw in array) {
JsonObject obj = (JsonObject)raw;
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);
}
// 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;
return defs;
}
public static void Save(bool global, Level lvl) {
BlockDefinition[] defs = global ? GlobalDefs : lvl.CustomBlockDefs;
// 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);
if (elems == null) elems = ConfigElement.GetAll(typeof(BlockDefinition));
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() {
@ -141,8 +162,8 @@ namespace MCGalaxy {
if (File.Exists(GlobalPath)) {
File.Copy(GlobalPath, GlobalBackupPath, true);
}
} catch (Exception ex) {
Logger.LogError("Error backing up global block defs", ex);
} catch (Exception ex) {
Logger.LogError("Error backing up global block defs", ex);
}
// As the BlockDefinition instances in levels will now be different
@ -160,14 +181,14 @@ namespace MCGalaxy {
if ((lvl.Props[b].ChangedScope & 2) == 0) {
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) {
BlockID_ block = def.GetBlock();
BlockID block = def.GetBlock();
bool global = defs == GlobalDefs;
if (global) UpdateGlobalCustom(block, def);
@ -178,7 +199,7 @@ namespace MCGalaxy {
Player[] players = PlayerInfo.Online.Items;
foreach (Player pl in players) {
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;
pl.Send(def.MakeDefinePacket(pl));
@ -188,7 +209,7 @@ namespace MCGalaxy {
}
public static void Remove(BlockDefinition def, BlockDefinition[] defs, Level level) {
BlockID_ block = def.GetBlock();
BlockID block = def.GetBlock();
bool global = defs == GlobalDefs;
if (global) UpdateGlobalCustom(block, null);
@ -199,7 +220,7 @@ namespace MCGalaxy {
Player[] players = PlayerInfo.Online.Items;
foreach (Player pl in players) {
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;
pl.Send(Packet.UndefineBlock(def, pl.hasExtBlocks));
@ -211,12 +232,12 @@ namespace MCGalaxy {
Player[] players = PlayerInfo.Online.Items;
foreach (Player pl in players) {
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);
}
}
static void UpdateGlobalCustom(BlockID_ block, BlockDefinition def) {
static void UpdateGlobalCustom(BlockID block, BlockDefinition def) {
Level[] loaded = LevelInfo.Loaded.Items;
foreach (Level lvl in loaded) {
if (lvl.CustomBlockDefs[block] != GlobalDefs[block]) continue;
@ -225,12 +246,11 @@ namespace MCGalaxy {
}
public void SetAllTex(ushort id) {
SetSideTex(id);
SetSideTex(id);
TopTex = id; BottomTex = id;
}
public void SetSideTex(ushort id) {
SideTex = id;
LeftTex = id; RightTex = id; FrontTex = id; BackTex = id;
}
@ -239,7 +259,7 @@ namespace MCGalaxy {
BlockDefinition[] defs = pl.level.CustomBlockDefs;
for (int i = 0; i < defs.Length; 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));
}
}
@ -258,23 +278,23 @@ namespace MCGalaxy {
// Fill slots with explicit order
for (int i = 0; i < defs.Length; 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 != 0) {
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
for (int i = 0; i < defs.Length; 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 (def != null && def.InventoryOrder >= 0) continue;
if (def != null && def.InventoryOrder >= 0) continue;
if (order_to_blocks[raw] == -1) {
order_to_blocks[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
for (int i = defs.Length - 1; i >= 0; 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 (block_to_orders[raw] != -1) continue;
@ -301,12 +321,12 @@ namespace MCGalaxy {
int order = block_to_orders[raw];
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;
// Special case, don't want 255 getting hidden by default
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;
foreach (Player pl in players) {
if (!global && pl.level != level) continue;

View File

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

View File

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

View File

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

View File

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

View File

@ -97,7 +97,6 @@ namespace MCGalaxy.Commands.CPE {
SetBD(p, global, new BlockDefinition());
BlockDefinition def = GetBD(p, global);
def.Version2 = true;
def.SetBlock(target);
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) {
p.Message(" Block is a sprite");
p.Message(" Texture ID: {0}", def.SideTex);
p.Message(" Texture ID: {0}", def.RightTex);
} else {
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);
@ -238,7 +237,7 @@ namespace MCGalaxy.Commands.CPE {
}
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) {
@ -254,7 +253,7 @@ namespace MCGalaxy.Commands.CPE {
BlockDefinition.Remove(def, defs, p.IsSuper ? null : p.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];
if (!global && globalDef != null)
@ -291,8 +290,8 @@ namespace MCGalaxy.Commands.CPE {
if (bd.Shape == 0) bd.SetAllTex(bd.TopTex);
}
} else if (step == 5) {
if (CommandParser.GetUShort(p, value, "Texture ID", ref bd.SideTex, 0, 255)) {
bd.SetSideTex(bd.SideTex);
if (CommandParser.GetUShort(p, value, "Texture ID", ref bd.RightTex, 0, 255)) {
bd.SetSideTex(bd.RightTex);
step++;
}
} else if (step == 6) {
@ -401,12 +400,12 @@ namespace MCGalaxy.Commands.CPE {
if (!EditUShort(p, value, "Top texture", ref def.TopTex, arg)) return;
break;
case "alltex":
if (!EditUShort(p, value, "All textures", ref def.SideTex, arg)) return;
def.SetAllTex(def.SideTex);
if (!EditUShort(p, value, "All textures", ref def.RightTex, arg)) return;
def.SetAllTex(def.RightTex);
break;
case "sidetex":
if (!EditUShort(p, value, "Side texture", ref def.SideTex, arg)) return;
def.SetSideTex(def.SideTex);
if (!EditUShort(p, value, "Side texture", ref def.RightTex, arg)) return;
def.SetSideTex(def.RightTex);
break;
case "lefttex":
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
if (order != def.BlockID && order != 0) {
if (order != def.RawID && order != 0) {
for (int i = 0; i < defs.Length; i++) {
if (defs[i] == null || defs[i].InventoryOrder != order) continue;
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.Save(global, level);
p.Message("Set inventory order for {0} to {1}", blockName,
order == def.BlockID ? "default" : order.ToString());
order == def.RawID ? "default" : order.ToString());
return;
default:
p.Message("Unrecognised property: " + arg); return;
@ -528,7 +527,7 @@ namespace MCGalaxy.Commands.CPE {
}
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();
BlockDefinition.Add(def, defs, p.IsSuper ? null : p.level);

View File

@ -46,6 +46,8 @@ namespace MCGalaxy {
ConfigElement elem;
elem.Field = field;
elem.Attrib = (ConfigAttribute)attributes[0];
if (elem.Attrib.Name == null) elem.Attrib.Name = field.Name;
elems.Add(elem);
}
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 {
int defValue, minValue, maxValue;
public ConfigIntAttribute()
: this(null, null, 0, int.MinValue, int.MaxValue) { }
public ConfigIntAttribute(string name, string section, int def,
int min = int.MinValue, int max = int.MaxValue)
: base(name, section) { defValue = def; minValue = min; maxValue = max; }
@ -47,8 +49,7 @@ namespace MCGalaxy.Config {
}
// Hacky workaround for old ExponentialFog attribute
public sealed class ConfigBoolIntAttribute : ConfigIntAttribute {
sealed class ConfigBoolIntAttribute : ConfigIntAttribute {
public ConfigBoolIntAttribute(string name, string section, int defValue)
: 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)
: 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 {
float defValue, minValue, maxValue;
public ConfigRealAttribute()
: this(null, null, 0, float.NegativeInfinity, float.PositiveInfinity) { }
public ConfigRealAttribute(string name, string section, float def,
float min = float.NegativeInfinity, float max = float.PositiveInfinity)
: base(name, section) { defValue = def; minValue = min; maxValue = max; }

View File

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

View File

@ -56,8 +56,11 @@ namespace MCGalaxy.Config {
public ConfigStringAttribute(string name, string section, string def)
: base(name, section) { defValue = def; }
public ConfigStringAttribute()
: base(null, null) { allowEmpty = true; }
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);
return defValue;
}

View File

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

View File

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

View File

@ -16,11 +16,12 @@
permissions and limitations under the Licenses.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using MCGalaxy.Config;
using MCGalaxy.Events.ServerEvents;
using Newtonsoft.Json;
namespace MCGalaxy.Network {
@ -62,7 +63,7 @@ namespace MCGalaxy.Network {
OnSendingHeartbeatEvent.Call(this, ref name);
name = Colors.Strip(name);
return
return
"&port=" + ServerConfig.Port +
"&max=" + ServerConfig.MaxPlayers +
"&name=" + Uri.EscapeDataString(name) +
@ -76,43 +77,52 @@ namespace MCGalaxy.Network {
public override void OnRequest(HttpWebRequest request) {
if (proxyUrl == null) return;
request.Proxy = new WebProxy(proxyUrl);
}
}
public override void OnResponse(string response) {
if (String.IsNullOrEmpty(response)) return;
// in form of http://www.classicube.net/server/play/<hash>/
if (response.EndsWith("/"))
if (response.EndsWith("/"))
response = response.Substring(0, response.Length - 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
if (String.IsNullOrEmpty(Server.Hash) || hash != Server.Hash) {
Server.Hash = hash;
Server.URL = response;
// only need to do this when contents have changed
if (hash == Server.Hash) return;
Server.Hash = hash;
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.UpdateUrl(Server.URL);
File.WriteAllText("text/externalurl.txt", Server.URL);
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)
Server.URL = resp.errors[0][0];
else
Server.URL = "Error while finding URL. Is the port open?";
Server.UpdateUrl(Server.URL);
Logger.Log(LogType.Warning, response);
Server.URL = error;
Server.UpdateUrl(Server.URL);
Logger.Log(LogType.Warning, response);
}
}
static string GetError(string json) {
JsonContext ctx = new JsonContext(); ctx.Val = json;
JsonObject obj = (JsonObject)Json.ParseStream(ctx);
if (obj == null) return null;
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];
}
}
}
#pragma warning disable 0649
class Response {
public string[][] errors;
public string response;
public string status;
return null;
}
#pragma warning restore 0649
}
}

View File

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

View File

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

View File

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

View File

@ -20,9 +20,7 @@ using System.Collections.Generic;
using System.Data;
using System.IO;
using MCGalaxy.Bots;
using MCGalaxy.DB;
using MCGalaxy.SQL;
using Newtonsoft.Json;
namespace MCGalaxy.Tasks {
internal static class UpgradeTasks {
@ -137,12 +135,12 @@ namespace MCGalaxy.Tasks {
Logger.Log(LogType.SystemActivity, "Making bots file per-level.. " +
"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>>();
foreach (BotProperties bot in bots) {
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)) {
levelBots = new List<BotProperties>();
@ -152,8 +150,10 @@ namespace MCGalaxy.Tasks {
}
foreach (var kvp in botsByLevel) {
json = JsonConvert.SerializeObject(kvp.Value);
File.WriteAllText(BotsFile.BotsPath(kvp.Key), json);
string path = BotsFile.BotsPath(kvp.Key);
using (StreamWriter w = new StreamWriter(path)) {
BotsFile.WriteAll(w, kvp.Value);
}
}
if (Server.mainLevel.Bots.Count == 0) {