[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
* pause
* game state: config, map, entities, players
* game menu pack progress

View File

@@ -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',
]
}),

View File

@@ -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();

View File

@@ -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> {

View File

@@ -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();

View File

@@ -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();

View File

@@ -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>;
}

View File

@@ -123,19 +123,26 @@ 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>;
}
typedef ConfigSource = {
var game:GameConfig;
var map: MapConfig;
var bricks: Array<BrickConfig>;
var presets: Array<GamePreset>;
var points: Array<SpawnPoint>;
var tanks: Array<TankConfig>;
var bonuses: Array<BonusConfig>;
var map:MapConfig;
var bricks:Array<BrickConfig>;
var presets:Array<GamePreset>;
var points:Array<SpawnPoint>;
var tanks:Array<TankConfig>;
var bonuses:Array<BonusConfig>;
}
class Config {

View File

@@ -43,4 +43,8 @@ class PackProgress {
}
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.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();
}
}

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 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;
}

View File

@@ -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);
}

View File

@@ -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();
});
}
}

View File

@@ -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;

View File

@@ -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> {