From 04bea46b3b87753c7a75c70302e99988dd0881ab Mon Sep 17 00:00:00 2001 From: shmyga Date: Tue, 22 Oct 2019 18:00:36 +0300 Subject: [PATCH] [common] add LevelPackMeta --- WORK.md | 1 + gulpfile.js | 2 +- src/client/haxe/ru/m/tankz/Init.hx | 2 +- .../ru/m/tankz/bundle/ClientLevelSource.hx | 13 ++- .../m/tankz/view/common/NetworkStateView.hx | 13 +-- .../ru/m/tankz/bundle/CachedLevelBundle.hx | 25 +++--- .../haxe/ru/m/tankz/bundle/ILevelBundle.hx | 1 + src/common/haxe/ru/m/tankz/config/Config.hx | 19 +++-- .../haxe/ru/m/tankz/game/PackProgress.hx | 4 + src/common/haxe/ru/m/tankz/util/LevelUtil.hx | 76 +++++++++--------- src/common/haxe/ru/m/tankz/util/ZipUtil.hx | 36 +++++++++ src/common/resources/level/classic_modern.zip | Bin 5399 -> 5955 bytes .../resources/level/classic_standard.zip | Bin 8918 -> 9102 bytes src/common/resources/level/death_standard.zip | Bin 1396 -> 1568 bytes src/common/resources/level/dota_standard.zip | Bin 9124 -> 9232 bytes .../haxe/ru/m/tankz/editor/EditorStorage.hx | 6 +- .../haxe/ru/m/tankz/editor/view/PackFrame.hx | 1 + .../ru/m/tankz/editor/view/PackListFrame.hx | 37 ++++----- .../haxe/ru/m/tankz/editor/view/PackView.hx | 18 ++--- .../tankz/server/bundle/ServerLevelSource.hx | 16 ++-- 20 files changed, 157 insertions(+), 113 deletions(-) create mode 100644 src/common/haxe/ru/m/tankz/util/ZipUtil.hx diff --git a/WORK.md b/WORK.md index 13fdd28..8bdeeb8 100644 --- a/WORK.md +++ b/WORK.md @@ -15,3 +15,4 @@ * game panel rework * pause * game state: config, map, entities, players +* game menu pack progress diff --git a/gulpfile.js b/gulpfile.js index 9b94d41..82f603d 100755 --- a/gulpfile.js +++ b/gulpfile.js @@ -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', ] }), diff --git a/src/client/haxe/ru/m/tankz/Init.hx b/src/client/haxe/ru/m/tankz/Init.hx index 5df4f35..79df69a 100644 --- a/src/client/haxe/ru/m/tankz/Init.hx +++ b/src/client/haxe/ru/m/tankz/Init.hx @@ -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(); diff --git a/src/client/haxe/ru/m/tankz/bundle/ClientLevelSource.hx b/src/client/haxe/ru/m/tankz/bundle/ClientLevelSource.hx index df976e3..858e801 100644 --- a/src/client/haxe/ru/m/tankz/bundle/ClientLevelSource.hx +++ b/src/client/haxe/ru/m/tankz/bundle/ClientLevelSource.hx @@ -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 { diff --git a/src/client/haxe/ru/m/tankz/view/common/NetworkStateView.hx b/src/client/haxe/ru/m/tankz/view/common/NetworkStateView.hx index b24627d..7e635f5 100644 --- a/src/client/haxe/ru/m/tankz/view/common/NetworkStateView.hx +++ b/src/client/haxe/ru/m/tankz/view/common/NetworkStateView.hx @@ -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(); diff --git a/src/common/haxe/ru/m/tankz/bundle/CachedLevelBundle.hx b/src/common/haxe/ru/m/tankz/bundle/CachedLevelBundle.hx index da72ebc..d470b26 100644 --- a/src/common/haxe/ru/m/tankz/bundle/CachedLevelBundle.hx +++ b/src/common/haxe/ru/m/tankz/bundle/CachedLevelBundle.hx @@ -26,18 +26,21 @@ 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); - cache.set(id, result); - return result; - } else { - var result = source.resolve(id); - if (storage != null) { - storage.write(id, result); - } - cache.set(id, result); - return result; } + 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; + } + } + var result = source.resolve(id); + if (storage != null) { + storage.write(id, result); + } + cache.set(id, result); + return result; } public function list():Array { diff --git a/src/common/haxe/ru/m/tankz/bundle/ILevelBundle.hx b/src/common/haxe/ru/m/tankz/bundle/ILevelBundle.hx index 1d6a4da..1533fb8 100644 --- a/src/common/haxe/ru/m/tankz/bundle/ILevelBundle.hx +++ b/src/common/haxe/ru/m/tankz/bundle/ILevelBundle.hx @@ -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; } diff --git a/src/common/haxe/ru/m/tankz/config/Config.hx b/src/common/haxe/ru/m/tankz/config/Config.hx index e697fcd..21e6160 100644 --- a/src/common/haxe/ru/m/tankz/config/Config.hx +++ b/src/common/haxe/ru/m/tankz/config/Config.hx @@ -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; } typedef ConfigSource = { var game:GameConfig; - var map: MapConfig; - var bricks: Array; - var presets: Array; - var points: Array; - var tanks: Array; - var bonuses: Array; + var map:MapConfig; + var bricks:Array; + var presets:Array; + var points:Array; + var tanks:Array; + var bonuses:Array; } class Config { diff --git a/src/common/haxe/ru/m/tankz/game/PackProgress.hx b/src/common/haxe/ru/m/tankz/game/PackProgress.hx index 91929ad..5484446 100644 --- a/src/common/haxe/ru/m/tankz/game/PackProgress.hx +++ b/src/common/haxe/ru/m/tankz/game/PackProgress.hx @@ -43,4 +43,8 @@ class PackProgress { } completed[levelId].presets[presetId] = result; } + + public function toString():String { + return 'PackProgress(id=${id}, completed=${completed}})'; + } } diff --git a/src/common/haxe/ru/m/tankz/util/LevelUtil.hx b/src/common/haxe/ru/m/tankz/util/LevelUtil.hx index c8ed405..5bcb5a9 100644 --- a/src/common/haxe/ru/m/tankz/util/LevelUtil.hx +++ b/src/common/haxe/ru/m/tankz/util/LevelUtil.hx @@ -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 - } - var level = LevelUtil.loads(bytes.toString()); - if (level.id == null) { - level.id = Std.parseInt(entry.fileName.split("level").pop()); - } - return level; - } - - 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 { + public static function getMeta(bytes:Bytes):LevelPackMeta { var files = haxe.zip.Reader.readZip(new BytesInput(bytes)); - return Lambda.array(files.map(extract)); + for (entry in files) { + if (entry.fileName == "meta.yml") { + var content = ZipUtil.extractEntry(entry); + return Yaml.parse(content, Parser.options().useObjects()); + } + } + return null; } - public static function pack(data:Array):Bytes { + public static function unpack(bytes:Bytes):LevelPack { + var files = haxe.zip.Reader.readZip(new BytesInput(bytes)); + var meta = null; + var data:Array = []; + 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()); + } + data.push(level); + } + } + return { + id: meta.id, + meta: meta, + data: data, + } + } + + 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 = pack.data.map(function(level) return ZipUtil.createEntry('${formatLevel(level.id)}.yml', dumps(level))); + writer.write(Lambda.list([metaEntry].concat(levelEntries))); return output.getBytes(); } } diff --git a/src/common/haxe/ru/m/tankz/util/ZipUtil.hx b/src/common/haxe/ru/m/tankz/util/ZipUtil.hx new file mode 100644 index 0000000..abaa6b9 --- /dev/null +++ b/src/common/haxe/ru/m/tankz/util/ZipUtil.hx @@ -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; + } +} diff --git a/src/common/resources/level/classic_modern.zip b/src/common/resources/level/classic_modern.zip index c8ea34ab70ca34a9ed0adadab099f0a54a14f9b1..fca9f58a89fff44a7e532dec8e3d3640786a4fee 100644 GIT binary patch literal 5955 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 MP>hcYuADyo4;-z7`2YX_ literal 5399 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 literal 8918 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 literal 1396 zcmWIWW@Zs#U|`^2h-vopzqzJY<13JNot1%s9Y`A(80b~z<}96jp6ieS&$0K_T@NbR zMcFTH4xjH}b42$>(sf?(gZ4k(<-M8_9W4{Pp!J*fewFy>W=qZ+0-NqAEf<=h)pyEC zavjSXIdjI(5=N!Vri3Q4%()s^#B(a8q&9p0OwMA)72cg+AB9~Da4W7lX27>GVrvg` zp+wToJVEy}#o~)SjkZngF=02_<+NhW6ulni%X1{7Pu+ccg2Ux`mcgRzwxJ z_r5PDfB941_vN$BFZ^!(G}gEJ{KNklVxh0%{`(YPw5nnZKna-#hr<~j07FEM1r{=f z;Et$ioyE4Q^2{$i7fD^6@6p^dnz@DPpYD@+9!8Jk zI)2=r5cv@A^w$nx=J5(vniWx&O`X@G_^Z(Oca-Tt4>PmvK9`N=nt@s_2VJ z+YGOL6}p!v&Kb#dtK`Q^pJdgNWg4+u$Fgj^6Ex?@3-X2PI)>)1Y&*KNcEg+NwygaY z|GF*MjElY5Z5ffE>bx8Z?j3<|uplkavl z8-N4v;OiKcqXD0^y!9{nW;mI8$-daO^!>h0#hm&#t2VML3G>%H?lBHC>2jWz9-FQ6 zO=jP^bUtaOOEOo@XI(3@n6=l9>6q8o!;L}(=bIR`E*1!sE^pmE$NIQqBELD4T%eDV znc&9Y8EzUGE^k~f_ZX&$98z*#RrSaE1yjeXs^INqJcq@u6jxprGTXmeYv&9PhrefT zqy;S~_#DKO)ExI%bGntG*tukz>tDO_dY0c2nBKg~Q8BH&>U~vM^Cv&<^F9gx zZufqNwAQLsAI`mgFZX)x^R~&)^iJ2XEI zYx};apRM0M>i%X8@MdHZVZdD$0W%*GfR;??n$WW;LWdME=nV|OnHF6Wda_1nl4k;G l0w;2GP3X~y&=kWA(gcp&0B=?{kOT`5UIEfwz|w<(0RRp)KG^^O diff --git a/src/common/resources/level/dota_standard.zip b/src/common/resources/level/dota_standard.zip index 8685991eca6c83cf6a5064479df4efd578cfdb2d..9e4165e2fde2b541c495d29ebbcd7c7cb9f28333 100644 GIT binary patch literal 9232 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 { + public function packList():Array { var result = []; var data:DynamicAccess = so.data; for (id in data.keys()) { - result.push(PackId.fromString(id)); + result.push(read(id)); } return result; } diff --git a/src/editor/haxe/ru/m/tankz/editor/view/PackFrame.hx b/src/editor/haxe/ru/m/tankz/editor/view/PackFrame.hx index 06c4d41..99952da 100644 --- a/src/editor/haxe/ru/m/tankz/editor/view/PackFrame.hx +++ b/src/editor/haxe/ru/m/tankz/editor/view/PackFrame.hx @@ -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); } diff --git a/src/editor/haxe/ru/m/tankz/editor/view/PackListFrame.hx b/src/editor/haxe/ru/m/tankz/editor/view/PackListFrame.hx index 70cae0a..978af77 100644 --- a/src/editor/haxe/ru/m/tankz/editor/view/PackListFrame.hx +++ b/src/editor/haxe/ru/m/tankz/editor/view/PackListFrame.hx @@ -14,7 +14,7 @@ import ru.m.tankz.util.LevelUtil; @:template class PackListFrame extends FrameView { public static inline var ID = "pack_list"; - @:view("packs") var packView:ActionDataView; + @:view("packs") var packView:ActionDataView; @: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(); }); } } diff --git a/src/editor/haxe/ru/m/tankz/editor/view/PackView.hx b/src/editor/haxe/ru/m/tankz/editor/view/PackView.hx index bb2591a..2b37834 100644 --- a/src/editor/haxe/ru/m/tankz/editor/view/PackView.hx +++ b/src/editor/haxe/ru/m/tankz/editor/view/PackView.hx @@ -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 { +@:template class PackView extends HGroupView implements IListItemView { 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; + private var actionSignal(get, null):Signal2; @: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 { - var dataView:ActionDataView = cast parent; + private function get_actionSignal():Signal2 { + var dataView:ActionDataView = 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; diff --git a/src/server/haxe/ru/m/tankz/server/bundle/ServerLevelSource.hx b/src/server/haxe/ru/m/tankz/server/bundle/ServerLevelSource.hx index 5b48ce5..a6b3358 100644 --- a/src/server/haxe/ru/m/tankz/server/bundle/ServerLevelSource.hx +++ b/src/server/haxe/ru/m/tankz/server/bundle/ServerLevelSource.hx @@ -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 {