[common] add LevelPackMeta
This commit is contained in:
1
WORK.md
1
WORK.md
@@ -15,3 +15,4 @@
|
||||
* game panel rework
|
||||
* pause
|
||||
* game state: config, map, entities, players
|
||||
* game menu pack progress
|
||||
|
||||
@@ -139,7 +139,7 @@ const server = new Project(
|
||||
name: 'server',
|
||||
sources: ['src/server/haxe'],
|
||||
main: 'ru.m.tankz.server.Server',
|
||||
resources: [
|
||||
assets: [
|
||||
'src/common/resources/level',
|
||||
]
|
||||
}),
|
||||
|
||||
@@ -66,7 +66,7 @@ class Init {
|
||||
public static function init():Void {
|
||||
theme = new AppTheme();
|
||||
resources = new Resources();
|
||||
levelBundle = new CachedLevelBundle(new ClientLevelSource()/*, new SharedObjectStorage()*/); //ToDo: update
|
||||
levelBundle = new CachedLevelBundle(new ClientLevelSource(), new SharedObjectStorage());
|
||||
levelBundle.load();
|
||||
configBundle = new ConfigBundle();
|
||||
settingsStorage = new SettingsStorage();
|
||||
|
||||
@@ -11,15 +11,14 @@ class ClientLevelSource implements ILevelSource {
|
||||
|
||||
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 {
|
||||
var bytes = Assets.getBytes('level/${id}.zip');
|
||||
return {
|
||||
id: id,
|
||||
data: LevelUtil.unpack(bytes).map(function(level) {
|
||||
level.packId = id;
|
||||
return level;
|
||||
}),
|
||||
};
|
||||
return LevelUtil.unpack(bytes);
|
||||
}
|
||||
|
||||
public function list():Array<PackId> {
|
||||
|
||||
@@ -27,24 +27,25 @@ import ru.m.tankz.view.popup.LoginPopup;
|
||||
userLabel.text = "";
|
||||
loginButton.visible = false;
|
||||
logoutButton.visible = false;
|
||||
var skin:SpriteSkin = cast stateView.skin;
|
||||
switch state {
|
||||
case OFFLINE:
|
||||
cast(stateView.skin, SpriteSkin).background.color = "black";
|
||||
skin.background.color = "black";
|
||||
stateView.visible = false;
|
||||
visible = false;
|
||||
case CONNECT:
|
||||
cast(stateView.skin, SpriteSkin).background.color = "yellow";
|
||||
skin.background.color = "yellow";
|
||||
case CONNECTED:
|
||||
cast(stateView.skin, SpriteSkin).background.color = "gray";
|
||||
skin.background.color = "gray";
|
||||
loginButton.visible = true;
|
||||
case LOGIN:
|
||||
cast(stateView.skin, SpriteSkin).background.color = "yellow";
|
||||
skin.background.color = "yellow";
|
||||
case ONLINE(user):
|
||||
cast(stateView.skin, SpriteSkin).background.color = "green";
|
||||
skin.background.color = "green";
|
||||
userLabel.text = user.name;
|
||||
logoutButton.visible = true;
|
||||
case ERROR(error):
|
||||
cast(stateView.skin, SpriteSkin).background.color = "red";
|
||||
skin.background.color = "red";
|
||||
userLabel.text = Std.string(error).substr(0, 10);
|
||||
}
|
||||
stateView.toRedraw();
|
||||
|
||||
@@ -26,11 +26,15 @@ class CachedLevelBundle implements ILevelBundle {
|
||||
public function get(id:PackId):LevelPack {
|
||||
if (cache.exists(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);
|
||||
return result;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
var result = source.resolve(id);
|
||||
if (storage != null) {
|
||||
storage.write(id, result);
|
||||
@@ -38,7 +42,6 @@ class CachedLevelBundle implements ILevelBundle {
|
||||
cache.set(id, result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public function list():Array<PackId> {
|
||||
return source.list();
|
||||
|
||||
@@ -5,6 +5,7 @@ import ru.m.tankz.Type;
|
||||
|
||||
interface ILevelSource {
|
||||
public function resolve(id:PackId):LevelPack;
|
||||
public function resolveMeta(id:PackId):LevelPackMeta;
|
||||
public function list():Array<PackId>;
|
||||
}
|
||||
|
||||
|
||||
@@ -123,8 +123,15 @@ typedef LevelConfig = {
|
||||
@:optional var size:{width:Int, height:Int};
|
||||
}
|
||||
|
||||
typedef LevelPackMeta = {
|
||||
var id:PackId;
|
||||
var author:String;
|
||||
var date:Date;
|
||||
}
|
||||
|
||||
typedef LevelPack = {
|
||||
var id:PackId;
|
||||
var meta:LevelPackMeta;
|
||||
var data:Array<LevelConfig>;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,4 +43,8 @@ class PackProgress {
|
||||
}
|
||||
completed[levelId].presets[presetId] = result;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return 'PackProgress(id=${id}, completed=${completed}})';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import haxe.io.Bytes;
|
||||
import haxe.io.BytesInput;
|
||||
import haxe.io.BytesOutput;
|
||||
import haxe.zip.Entry;
|
||||
import haxe.zip.Tools;
|
||||
import haxe.zip.Writer;
|
||||
import ru.m.tankz.config.Config;
|
||||
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('');
|
||||
return Yaml.render({
|
||||
data: bricksStr,
|
||||
@@ -54,48 +53,47 @@ class LevelUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static function extract(entry:Entry):LevelConfig {
|
||||
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
|
||||
public static function getMeta(bytes:Bytes):LevelPackMeta {
|
||||
var files = haxe.zip.Reader.readZip(new BytesInput(bytes));
|
||||
for (entry in files) {
|
||||
if (entry.fileName == "meta.yml") {
|
||||
var content = ZipUtil.extractEntry(entry);
|
||||
return Yaml.parse(content, Parser.options().useObjects());
|
||||
}
|
||||
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) {
|
||||
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 {
|
||||
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 {
|
||||
public static function pack(pack:LevelPack):Bytes {
|
||||
var output = new BytesOutput();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
36
src/common/haxe/ru/m/tankz/util/ZipUtil.hx
Normal file
36
src/common/haxe/ru/m/tankz/util/ZipUtil.hx
Normal 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;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,7 +2,7 @@ package ru.m.tankz.editor;
|
||||
|
||||
import haxe.DynamicAccess;
|
||||
import haxework.storage.SharedObjectStorage;
|
||||
import ru.m.tankz.Type;
|
||||
import ru.m.tankz.config.Config;
|
||||
|
||||
class EditorStorage extends SharedObjectStorage {
|
||||
private static inline var VERSION = 2;
|
||||
@@ -11,11 +11,11 @@ class EditorStorage extends SharedObjectStorage {
|
||||
super('editor_${VERSION}');
|
||||
}
|
||||
|
||||
public function packList():Array<PackId> {
|
||||
public function packList():Array<LevelPack> {
|
||||
var result = [];
|
||||
var data:DynamicAccess<String> = so.data;
|
||||
for (id in data.keys()) {
|
||||
result.push(PackId.fromString(id));
|
||||
result.push(read(id));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ using ru.m.tankz.view.ViewUtil;
|
||||
pack.data[data.id] = data;
|
||||
levels.data = pack.data;
|
||||
level = data;
|
||||
pack.meta.date = Date.now();
|
||||
storage.write(pack.id, pack);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import ru.m.tankz.util.LevelUtil;
|
||||
@:template class PackListFrame extends FrameView<Dynamic> {
|
||||
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 switcher:FrameSwitcher;
|
||||
@@ -29,18 +29,19 @@ import ru.m.tankz.util.LevelUtil;
|
||||
packView.data = storage.packList();
|
||||
}
|
||||
|
||||
private function onPackAction(value:PackId, action:PackAction):Void {
|
||||
private function onPackAction(value:LevelPack, action:PackAction):Void {
|
||||
switch action {
|
||||
case EXPORT:
|
||||
var pack:LevelPack = storage.read(value);
|
||||
FileUtil.save({
|
||||
name: '${value}.zip',
|
||||
content: LevelUtil.pack(pack.data),
|
||||
name: '${value.id}.zip',
|
||||
content: LevelUtil.pack(value),
|
||||
});
|
||||
storage.write(value.id, value);
|
||||
packView.data = storage.packList();
|
||||
case EDIT:
|
||||
switcher.change(PackFrame.ID, storage.read(value));
|
||||
switcher.change(PackFrame.ID, value);
|
||||
case DELETE:
|
||||
storage.delete(value);
|
||||
storage.delete(value.id);
|
||||
packView.data = storage.packList();
|
||||
}
|
||||
}
|
||||
@@ -48,30 +49,22 @@ import ru.m.tankz.util.LevelUtil;
|
||||
private function create():Void {
|
||||
PackPopup.instance.show().then(function(packId:PackId) {
|
||||
if (packId != null) {
|
||||
var levelPack:LevelPack = {
|
||||
var pack:LevelPack = {
|
||||
id: packId,
|
||||
meta: {id: packId, author: "default", date: null},
|
||||
data: [],
|
||||
}
|
||||
storage.write(levelPack.id, levelPack);
|
||||
packView.data.push(levelPack.id);
|
||||
packView.data = packView.data;
|
||||
storage.write(pack.id, pack);
|
||||
packView.data = storage.packList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function open():Void {
|
||||
FileUtil.browse().then(function(file:FileContent) {
|
||||
var packId = PackId.fromArray(file.name.split("/").pop().split(".").shift().split("_"));
|
||||
var levelPack:LevelPack = {
|
||||
id: packId,
|
||||
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;
|
||||
var pack:LevelPack = LevelUtil.unpack(file.content);
|
||||
storage.write(pack.id, pack);
|
||||
packView.data = storage.packList();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import haxework.view.data.DataView;
|
||||
import haxework.view.form.ButtonView;
|
||||
import haxework.view.group.HGroupView;
|
||||
import haxework.view.list.ListView;
|
||||
import ru.m.tankz.Type;
|
||||
import ru.m.tankz.config.Config;
|
||||
|
||||
using ru.m.tankz.view.ViewUtil;
|
||||
|
||||
@@ -15,23 +15,23 @@ enum PackAction {
|
||||
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 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;
|
||||
|
||||
private function set_data(value:PackId):PackId {
|
||||
private function set_data(value:LevelPack):LevelPack {
|
||||
data = value;
|
||||
label.text = data.toPackLabel();
|
||||
label.text = data.id.toPackLabel() + " | " + data.meta.date;
|
||||
return data;
|
||||
}
|
||||
|
||||
private function get_actionSignal():Signal2<PackId, PackAction> {
|
||||
var dataView:ActionDataView<PackId, PackView, PackAction> = cast parent;
|
||||
private function get_actionSignal():Signal2<LevelPack, PackAction> {
|
||||
var dataView:ActionDataView<LevelPack, PackView, PackAction> = cast parent;
|
||||
return dataView.onDataAction;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ enum PackAction {
|
||||
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();
|
||||
result.item_index = index;
|
||||
result.data = value;
|
||||
|
||||
@@ -9,20 +9,20 @@ import sys.io.File;
|
||||
|
||||
class ServerLevelSource implements ILevelSource {
|
||||
|
||||
private static inline var PATH = "./resources/level";
|
||||
private static inline var PATH = "./level";
|
||||
|
||||
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 {
|
||||
var path = FileSystem.absolutePath('${PATH}/${id}.zip');
|
||||
var bytes = File.getBytes(path);
|
||||
return {
|
||||
id: id,
|
||||
data: LevelUtil.unpack(bytes).map(function(level) {
|
||||
level.packId = id;
|
||||
return level;
|
||||
}),
|
||||
};
|
||||
return LevelUtil.unpack(bytes);
|
||||
}
|
||||
|
||||
public function list():Array<PackId> {
|
||||
|
||||
Reference in New Issue
Block a user