[common] add LevelPackMeta

This commit is contained in:
2019-10-22 18:00:36 +03:00
parent 4da06e52e5
commit 04bea46b3b
20 changed files with 157 additions and 113 deletions

View File

@@ -15,3 +15,4 @@
* game panel rework * game panel rework
* pause * pause
* game state: config, map, entities, players * game state: config, map, entities, players
* game menu pack progress

View File

@@ -139,7 +139,7 @@ const server = new Project(
name: 'server', name: 'server',
sources: ['src/server/haxe'], sources: ['src/server/haxe'],
main: 'ru.m.tankz.server.Server', main: 'ru.m.tankz.server.Server',
resources: [ assets: [
'src/common/resources/level', 'src/common/resources/level',
] ]
}), }),

View File

@@ -66,7 +66,7 @@ class Init {
public static function init():Void { public static function init():Void {
theme = new AppTheme(); theme = new AppTheme();
resources = new Resources(); resources = new Resources();
levelBundle = new CachedLevelBundle(new ClientLevelSource()/*, new SharedObjectStorage()*/); //ToDo: update levelBundle = new CachedLevelBundle(new ClientLevelSource(), new SharedObjectStorage());
levelBundle.load(); levelBundle.load();
configBundle = new ConfigBundle(); configBundle = new ConfigBundle();
settingsStorage = new SettingsStorage(); settingsStorage = new SettingsStorage();

View File

@@ -11,15 +11,14 @@ class ClientLevelSource implements ILevelSource {
public function new() {} public function new() {}
public function resolveMeta(id:PackId):LevelPackMeta {
var bytes = Assets.getBytes('level/${id}.zip');
return LevelUtil.getMeta(bytes);
}
public function resolve(id:PackId):LevelPack { public function resolve(id:PackId):LevelPack {
var bytes = Assets.getBytes('level/${id}.zip'); var bytes = Assets.getBytes('level/${id}.zip');
return { return LevelUtil.unpack(bytes);
id: id,
data: LevelUtil.unpack(bytes).map(function(level) {
level.packId = id;
return level;
}),
};
} }
public function list():Array<PackId> { public function list():Array<PackId> {

View File

@@ -27,24 +27,25 @@ import ru.m.tankz.view.popup.LoginPopup;
userLabel.text = ""; userLabel.text = "";
loginButton.visible = false; loginButton.visible = false;
logoutButton.visible = false; logoutButton.visible = false;
var skin:SpriteSkin = cast stateView.skin;
switch state { switch state {
case OFFLINE: case OFFLINE:
cast(stateView.skin, SpriteSkin).background.color = "black"; skin.background.color = "black";
stateView.visible = false; stateView.visible = false;
visible = false; visible = false;
case CONNECT: case CONNECT:
cast(stateView.skin, SpriteSkin).background.color = "yellow"; skin.background.color = "yellow";
case CONNECTED: case CONNECTED:
cast(stateView.skin, SpriteSkin).background.color = "gray"; skin.background.color = "gray";
loginButton.visible = true; loginButton.visible = true;
case LOGIN: case LOGIN:
cast(stateView.skin, SpriteSkin).background.color = "yellow"; skin.background.color = "yellow";
case ONLINE(user): case ONLINE(user):
cast(stateView.skin, SpriteSkin).background.color = "green"; skin.background.color = "green";
userLabel.text = user.name; userLabel.text = user.name;
logoutButton.visible = true; logoutButton.visible = true;
case ERROR(error): case ERROR(error):
cast(stateView.skin, SpriteSkin).background.color = "red"; skin.background.color = "red";
userLabel.text = Std.string(error).substr(0, 10); userLabel.text = Std.string(error).substr(0, 10);
} }
stateView.toRedraw(); stateView.toRedraw();

View File

@@ -26,11 +26,15 @@ class CachedLevelBundle implements ILevelBundle {
public function get(id:PackId):LevelPack { public function get(id:PackId):LevelPack {
if (cache.exists(id)) { if (cache.exists(id)) {
return cache.get(id); return cache.get(id);
} else if (storage != null && storage.exists(id)) { }
var result = storage.read(id); if (storage != null && storage.exists(id)) {
var result:LevelPack = storage.read(id);
var meta = source.resolveMeta(id);
if (result.meta != null && result.meta.date.getTime() == meta.date.getTime()) {
cache.set(id, result); cache.set(id, result);
return result; return result;
} else { }
}
var result = source.resolve(id); var result = source.resolve(id);
if (storage != null) { if (storage != null) {
storage.write(id, result); storage.write(id, result);
@@ -38,7 +42,6 @@ class CachedLevelBundle implements ILevelBundle {
cache.set(id, result); cache.set(id, result);
return result; return result;
} }
}
public function list():Array<PackId> { public function list():Array<PackId> {
return source.list(); return source.list();

View File

@@ -5,6 +5,7 @@ import ru.m.tankz.Type;
interface ILevelSource { interface ILevelSource {
public function resolve(id:PackId):LevelPack; public function resolve(id:PackId):LevelPack;
public function resolveMeta(id:PackId):LevelPackMeta;
public function list():Array<PackId>; public function list():Array<PackId>;
} }

View File

@@ -123,8 +123,15 @@ typedef LevelConfig = {
@:optional var size:{width:Int, height:Int}; @:optional var size:{width:Int, height:Int};
} }
typedef LevelPackMeta = {
var id:PackId;
var author:String;
var date:Date;
}
typedef LevelPack = { typedef LevelPack = {
var id:PackId; var id:PackId;
var meta:LevelPackMeta;
var data:Array<LevelConfig>; var data:Array<LevelConfig>;
} }

View File

@@ -43,4 +43,8 @@ class PackProgress {
} }
completed[levelId].presets[presetId] = result; completed[levelId].presets[presetId] = result;
} }
public function toString():String {
return 'PackProgress(id=${id}, completed=${completed}})';
}
} }

View File

@@ -4,7 +4,6 @@ import haxe.io.Bytes;
import haxe.io.BytesInput; import haxe.io.BytesInput;
import haxe.io.BytesOutput; import haxe.io.BytesOutput;
import haxe.zip.Entry; import haxe.zip.Entry;
import haxe.zip.Tools;
import haxe.zip.Writer; import haxe.zip.Writer;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.Type; import ru.m.tankz.Type;
@@ -37,7 +36,7 @@ class LevelUtil {
} }
} }
public static function dumps(config:Config, level:LevelConfig):String { public static function dumps(level:LevelConfig):String {
var bricksStr = level.data.join(''); var bricksStr = level.data.join('');
return Yaml.render({ return Yaml.render({
data: bricksStr, data: bricksStr,
@@ -54,48 +53,47 @@ class LevelUtil {
} }
} }
private static function extract(entry:Entry):LevelConfig { public static function getMeta(bytes:Bytes):LevelPackMeta {
var bytes:Bytes = entry.data; var files = haxe.zip.Reader.readZip(new BytesInput(bytes));
if (entry.compressed) { for (entry in files) {
#if ((flash || html5) && lime) if (entry.fileName == "meta.yml") {
bytes = cast(bytes, lime.utils.Bytes).decompress(lime.utils.CompressionAlgorithm.DEFLATE); var content = ZipUtil.extractEntry(entry);
#else return Yaml.parse(content, Parser.options().useObjects());
bytes = haxe.zip.Reader.unzip(entry);
#end
} }
var level = LevelUtil.loads(bytes.toString()); }
return null;
}
public static function unpack(bytes:Bytes):LevelPack {
var files = haxe.zip.Reader.readZip(new BytesInput(bytes));
var meta = null;
var data:Array<LevelConfig> = [];
for (entry in files) {
var content = ZipUtil.extractEntry(entry);
if (entry.fileName == "meta.yml") {
meta = Yaml.parse(content, Parser.options().useObjects());
} else {
var level:LevelConfig = LevelUtil.loads(content);
level.packId = meta.id;
if (level.id == null) { if (level.id == null) {
level.id = Std.parseInt(entry.fileName.split("level").pop()); level.id = Std.parseInt(entry.fileName.split("level").pop());
} }
return level; data.push(level);
}
}
return {
id: meta.id,
meta: meta,
data: data,
}
} }
private static function compress(level:LevelConfig):Entry { public static function pack(pack:LevelPack):Bytes {
var content = LevelUtil.dumps(null, level);
var bytes = Bytes.ofString(content);
var crc = haxe.crypto.Crc32.make(bytes);
var result:Entry = {
fileName: '${formatLevel(level.id)}.yml',
fileSize: bytes.length,
fileTime: Date.now(),
compressed: false,
dataSize: bytes.length,
data: bytes,
crc32: crc,
};
Tools.compress(result, 9);
return result;
}
public static function unpack(bytes:Bytes):Array<LevelConfig> {
var files = haxe.zip.Reader.readZip(new BytesInput(bytes));
return Lambda.array(files.map(extract));
}
public static function pack(data:Array<LevelConfig>):Bytes {
var output = new BytesOutput(); var output = new BytesOutput();
var writer = new Writer(output); var writer = new Writer(output);
writer.write(Lambda.list(data.map(compress))); var metaEntry:Entry = ZipUtil.createEntry("meta.yml", Yaml.render(pack.meta, Renderer.options().setFlowLevel(1)));
var levelEntries:Array<Entry> = pack.data.map(function(level) return ZipUtil.createEntry('${formatLevel(level.id)}.yml', dumps(level)));
writer.write(Lambda.list([metaEntry].concat(levelEntries)));
return output.getBytes(); return output.getBytes();
} }
} }

View File

@@ -0,0 +1,36 @@
package ru.m.tankz.util;
import haxe.io.Bytes;
import haxe.zip.Entry;
import haxe.zip.Tools;
class ZipUtil {
public static function extractEntry(entry:Entry):String {
var bytes:Bytes = entry.data;
if (entry.compressed) {
#if ((flash || html5) && lime)
bytes = cast(bytes, lime.utils.Bytes).decompress(lime.utils.CompressionAlgorithm.DEFLATE);
#else
bytes = haxe.zip.Reader.unzip(entry);
#end
}
return bytes.toString();
}
public static function createEntry(name:String, content:String):Entry {
var bytes = Bytes.ofString(content);
var crc = haxe.crypto.Crc32.make(bytes);
var result:Entry = {
fileName: name,
fileSize: bytes.length,
fileTime: Date.now(),
compressed: false,
dataSize: bytes.length,
data: bytes,
crc32: crc,
};
Tools.compress(result, 9);
return result;
}
}

View File

@@ -2,7 +2,7 @@ package ru.m.tankz.editor;
import haxe.DynamicAccess; import haxe.DynamicAccess;
import haxework.storage.SharedObjectStorage; import haxework.storage.SharedObjectStorage;
import ru.m.tankz.Type; import ru.m.tankz.config.Config;
class EditorStorage extends SharedObjectStorage { class EditorStorage extends SharedObjectStorage {
private static inline var VERSION = 2; private static inline var VERSION = 2;
@@ -11,11 +11,11 @@ class EditorStorage extends SharedObjectStorage {
super('editor_${VERSION}'); super('editor_${VERSION}');
} }
public function packList():Array<PackId> { public function packList():Array<LevelPack> {
var result = []; var result = [];
var data:DynamicAccess<String> = so.data; var data:DynamicAccess<String> = so.data;
for (id in data.keys()) { for (id in data.keys()) {
result.push(PackId.fromString(id)); result.push(read(id));
} }
return result; return result;
} }

View File

@@ -118,6 +118,7 @@ using ru.m.tankz.view.ViewUtil;
pack.data[data.id] = data; pack.data[data.id] = data;
levels.data = pack.data; levels.data = pack.data;
level = data; level = data;
pack.meta.date = Date.now();
storage.write(pack.id, pack); storage.write(pack.id, pack);
} }

View File

@@ -14,7 +14,7 @@ import ru.m.tankz.util.LevelUtil;
@:template class PackListFrame extends FrameView<Dynamic> { @:template class PackListFrame extends FrameView<Dynamic> {
public static inline var ID = "pack_list"; public static inline var ID = "pack_list";
@:view("packs") var packView:ActionDataView<PackId, PackView, PackAction>; @:view("packs") var packView:ActionDataView<LevelPack, PackView, PackAction>;
@:provide static var levelBundle:ILevelBundle; @:provide static var levelBundle:ILevelBundle;
@:provide static var switcher:FrameSwitcher; @:provide static var switcher:FrameSwitcher;
@@ -29,18 +29,19 @@ import ru.m.tankz.util.LevelUtil;
packView.data = storage.packList(); packView.data = storage.packList();
} }
private function onPackAction(value:PackId, action:PackAction):Void { private function onPackAction(value:LevelPack, action:PackAction):Void {
switch action { switch action {
case EXPORT: case EXPORT:
var pack:LevelPack = storage.read(value);
FileUtil.save({ FileUtil.save({
name: '${value}.zip', name: '${value.id}.zip',
content: LevelUtil.pack(pack.data), content: LevelUtil.pack(value),
}); });
storage.write(value.id, value);
packView.data = storage.packList();
case EDIT: case EDIT:
switcher.change(PackFrame.ID, storage.read(value)); switcher.change(PackFrame.ID, value);
case DELETE: case DELETE:
storage.delete(value); storage.delete(value.id);
packView.data = storage.packList(); packView.data = storage.packList();
} }
} }
@@ -48,30 +49,22 @@ import ru.m.tankz.util.LevelUtil;
private function create():Void { private function create():Void {
PackPopup.instance.show().then(function(packId:PackId) { PackPopup.instance.show().then(function(packId:PackId) {
if (packId != null) { if (packId != null) {
var levelPack:LevelPack = { var pack:LevelPack = {
id: packId, id: packId,
meta: {id: packId, author: "default", date: null},
data: [], data: [],
} }
storage.write(levelPack.id, levelPack); storage.write(pack.id, pack);
packView.data.push(levelPack.id); packView.data = storage.packList();
packView.data = packView.data;
} }
}); });
} }
private function open():Void { private function open():Void {
FileUtil.browse().then(function(file:FileContent) { FileUtil.browse().then(function(file:FileContent) {
var packId = PackId.fromArray(file.name.split("/").pop().split(".").shift().split("_")); var pack:LevelPack = LevelUtil.unpack(file.content);
var levelPack:LevelPack = { storage.write(pack.id, pack);
id: packId, packView.data = storage.packList();
data: LevelUtil.unpack(file.content).map(function(level) {
level.packId = packId;
return level;
}),
};
storage.write(levelPack.id, levelPack);
packView.data.push(levelPack.id);
packView.data = packView.data;
}); });
} }
} }

View File

@@ -5,7 +5,7 @@ import haxework.view.data.DataView;
import haxework.view.form.ButtonView; import haxework.view.form.ButtonView;
import haxework.view.group.HGroupView; import haxework.view.group.HGroupView;
import haxework.view.list.ListView; import haxework.view.list.ListView;
import ru.m.tankz.Type; import ru.m.tankz.config.Config;
using ru.m.tankz.view.ViewUtil; using ru.m.tankz.view.ViewUtil;
@@ -15,23 +15,23 @@ enum PackAction {
DELETE; DELETE;
} }
@:template class PackView extends HGroupView implements IListItemView<PackId> { @:template class PackView extends HGroupView implements IListItemView<LevelPack> {
public var item_index(default, default):Int; public var item_index(default, default):Int;
public var data(default, set):PackId; public var data(default, set):LevelPack;
private var actionSignal(get, null):Signal2<PackId, PackAction>; private var actionSignal(get, null):Signal2<LevelPack, PackAction>;
@:view var label:ButtonView; @:view var label:ButtonView;
private function set_data(value:PackId):PackId { private function set_data(value:LevelPack):LevelPack {
data = value; data = value;
label.text = data.toPackLabel(); label.text = data.id.toPackLabel() + " | " + data.meta.date;
return data; return data;
} }
private function get_actionSignal():Signal2<PackId, PackAction> { private function get_actionSignal():Signal2<LevelPack, PackAction> {
var dataView:ActionDataView<PackId, PackView, PackAction> = cast parent; var dataView:ActionDataView<LevelPack, PackView, PackAction> = cast parent;
return dataView.onDataAction; return dataView.onDataAction;
} }
@@ -47,7 +47,7 @@ enum PackAction {
actionSignal.emit(data, DELETE); actionSignal.emit(data, DELETE);
} }
public static function factory(index:Int, value:PackId):PackView { public static function factory(index:Int, value:LevelPack):PackView {
var result = new PackView(); var result = new PackView();
result.item_index = index; result.item_index = index;
result.data = value; result.data = value;

View File

@@ -9,20 +9,20 @@ import sys.io.File;
class ServerLevelSource implements ILevelSource { class ServerLevelSource implements ILevelSource {
private static inline var PATH = "./resources/level"; private static inline var PATH = "./level";
public function new() {} public function new() {}
public function resolveMeta(id:PackId):LevelPackMeta {
var path = FileSystem.absolutePath('${PATH}/${id}.zip');
var bytes = File.getBytes(path);
return LevelUtil.getMeta(bytes);
}
public function resolve(id:PackId):LevelPack { public function resolve(id:PackId):LevelPack {
var path = FileSystem.absolutePath('${PATH}/${id}.zip'); var path = FileSystem.absolutePath('${PATH}/${id}.zip');
var bytes = File.getBytes(path); var bytes = File.getBytes(path);
return { return LevelUtil.unpack(bytes);
id: id,
data: LevelUtil.unpack(bytes).map(function(level) {
level.packId = id;
return level;
}),
};
} }
public function list():Array<PackId> { public function list():Array<PackId> {