[common] add CachedLevelBundle; [client] add NetworkStateView

This commit is contained in:
2019-09-23 14:26:38 +03:00
parent beda5e6c5f
commit d2aff5f2ee
21 changed files with 261 additions and 95 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "tankz",
"version": "0.17.2",
"version": "0.17.3",
"private": true,
"devDependencies": {
"dateformat": "^3.0.3",

View File

@@ -58,6 +58,12 @@ class AppTheme extends Theme {
"geometry.height" => SizeValue.fromInt(50),
], ["button"]));
register(new Style("button.small", [
"font.size" => 14,
"geometry.padding" => Box.fromArray([8, 2]),
"skin.round" => 5,
], ["button"]));
register(new Style("text.box", [
"skin.background.color" => Color.fromInt(0x000000),
"skin.background.alpha" => 0.1,

View File

@@ -7,6 +7,7 @@ import haxework.net.manage.ILoaderManager;
import haxework.net.manage.LoaderManager;
import haxework.resources.IResources;
import haxework.resources.Resources;
import haxework.storage.SharedObjectStorage;
import haxework.view.popup.PopupManager;
import haxework.view.theme.ITheme;
import lime.ui.Gamepad;
@@ -16,10 +17,11 @@ import ru.m.control.ControlBus;
import ru.m.control.IControlBus;
import ru.m.control.JoystickDevice;
import ru.m.control.KeyboardDevice;
import ru.m.tankz.bundle.CachedLevelBundle;
import ru.m.tankz.bundle.ClientLevelSource;
import ru.m.tankz.bundle.ConfigBundle;
import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.bundle.ILevelBundle;
import ru.m.tankz.bundle.LevelBundle;
import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.proto.pack.Request;
import ru.m.tankz.proto.pack.Response;
@@ -63,7 +65,8 @@ class Init {
public static function init():Void {
theme = new AppTheme();
resources = new Resources();
levelBundle = new LevelBundle();
levelBundle = new CachedLevelBundle(new ClientLevelSource(), new SharedObjectStorage());
levelBundle.load();
configBundle = new ConfigBundle();
settingsStorage = new SettingsStorage();
multiplayerStorage = new NetworkStorage();

View File

@@ -2,17 +2,16 @@ package ru.m.tankz.bundle;
import openfl.Assets;
import openfl.utils.AssetType;
import ru.m.tankz.bundle.ILevelBundle;
import ru.m.tankz.config.Config;
import ru.m.tankz.Type;
import ru.m.tankz.util.LevelUtil;
class LevelBundle implements ILevelBundle {
private var _cache:Map<String, LevelPack> = new Map();
class ClientLevelSource implements ILevelSource {
public function new() {}
private function resolve(id:PackId):LevelPack {
public function resolve(id:PackId):LevelPack {
var bytes = Assets.getBytes('level/${id}.zip');
return {
id: id,
@@ -23,13 +22,6 @@ class LevelBundle implements ILevelBundle {
};
}
public function get(id:PackId):LevelPack {
if (!_cache.exists(id)) {
_cache.set(id, resolve(id));
}
return _cache.get(id);
}
public function list():Array<PackId> {
var result = [];
for (path in Assets.list(AssetType.BINARY)) {

View File

@@ -1,14 +1,12 @@
package ru.m.tankz.network;
import ru.m.tankz.proto.game.GameEventProto;
import ru.m.tankz.proto.room.SlotProto;
import ru.m.tankz.proto.room.SlotRequest;
import haxe.Serializer;
import haxework.signal.Signal;
import ru.m.connect.IConnection;
import ru.m.tankz.control.Control;
import ru.m.tankz.game.GameEvent;
import ru.m.tankz.proto.core.UserProto;
import ru.m.tankz.proto.game.GameEventProto;
import ru.m.tankz.proto.pack.GameEventRequest;
import ru.m.tankz.proto.pack.GameEventResponse;
import ru.m.tankz.proto.pack.LoginRequest;
@@ -21,8 +19,11 @@ import ru.m.tankz.proto.room.LeaveRequest;
import ru.m.tankz.proto.room.RoomListRequest;
import ru.m.tankz.proto.room.RoomProto;
import ru.m.tankz.proto.room.RoomRequest;
import ru.m.tankz.proto.room.SlotProto;
import ru.m.tankz.proto.room.SlotRequest;
import ru.m.tankz.proto.room.StartRequest;
import ru.m.tankz.storage.NetworkStorage;
import ru.m.tankz.storage.SettingsStorage;
typedef ClientConnection = IConnection<Request, Response>;
@@ -37,6 +38,8 @@ enum ConnectionState {
class NetworkManager {
private static inline var TAG = "NetworkManager";
public var state(default, null):ConnectionState;
public var room(default, null):RoomProto;
public var user(default, null):UserProto;
@@ -48,6 +51,7 @@ class NetworkManager {
@:provide private var connection:ClientConnection;
@:provide private var storage:NetworkStorage;
@:provide private var settings:SettingsStorage;
private var reconnectTimer:Timer;
private var reconnectDelay:Int;
@@ -61,10 +65,26 @@ class NetworkManager {
updateState(OFFLINE);
connection.handler.connect(onConnectionEvent);
connection.receiveHandler.connect(onResponse);
//connect(); // ToDo: disable network
if (settings.enableNetwork) {
connect();
}
settings.signal.connect(onSettingChange);
}
private function onSettingChange(setting:Setting):Void {
switch setting {
case ENABLE_NETWORK(value):
if (value) {
connect();
} else {
disconnect();
}
case _:
}
}
private function updateState(value:ConnectionState):Void {
L.d(TAG, 'State: ${value}');
state = value;
stateSignal.emit(value);
}
@@ -131,6 +151,10 @@ class NetworkManager {
}
}
private function disconnect():Void {
connection.disconnect();
}
private function onConnectionEvent(event:ConnectionEvent):Void {
updateState(switch event {
case CONNECTED: CONNECTED;
@@ -145,7 +169,9 @@ class NetworkManager {
login(user.name, user.uuid);
}
case DISCONNECTED | ERROR(_):
reconnect();
if (settings.enableNetwork) {
reconnect();
}
}
}

View File

@@ -34,6 +34,18 @@ class SettingsStorage extends SharedObjectStorage {
return get_displayFPS();
}
public var enableNetwork(get, set):Bool;
private function get_enableNetwork():Bool {
return read("setting:enable_network");
}
private function set_enableNetwork(value:Bool):Bool {
write("setting:enable_network", value);
signal.emit(ENABLE_NETWORK(value));
return get_enableNetwork();
}
public function new() {
super('settings_${VERSION}');
}

View File

@@ -14,3 +14,7 @@ views:
_record_: {$class: ru.m.tankz.view.RecordFrame}
_room_list_: {$class: ru.m.tankz.view.network.RoomListFrame}
_room_: {$class: ru.m.tankz.view.network.RoomFrame}
- $type: ru.m.tankz.view.common.NetworkStateView
geometry.position: absolute
geometry.hAlign: center
geometry.margin.top: 2

View File

@@ -9,6 +9,7 @@ import ru.m.tankz.bundle.ILevelBundle;
import ru.m.tankz.game.GameInit;
import ru.m.tankz.game.GameState;
import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.storage.SettingsStorage;
import ru.m.tankz.Type;
import ru.m.tankz.view.common.PackView;
import ru.m.tankz.view.network.RoomFrame;
@@ -26,12 +27,11 @@ using ru.m.tankz.view.ViewUtil;
@:provide static var network:NetworkManager;
@:provide static var appUpdater:Updater;
@:provide static var levelBundle:ILevelBundle;
@:provide static var settings:SettingsStorage;
@:view var packs:DataView<PackId, PackView>;
@:view var username:LabelView;
@:view("login") var loginButton:ButtonView;
@:view("logout") var logoutButton:ButtonView;
@:view("update") var updateButton:ButtonView;
@:view("network") var networkButton:ButtonView;
public function new() {
super(ID);
@@ -39,8 +39,6 @@ using ru.m.tankz.view.ViewUtil;
override public function onShow(data:Dynamic):Void {
super.onShow(data);
network.stateSignal.connect(onConnectionState);
onConnectionState(network.state);
appUpdater.check().then(function(update:Bool) {
updateButton.visible = update;
if (update) {
@@ -50,11 +48,20 @@ using ru.m.tankz.view.ViewUtil;
var list = levelBundle.list();
list.sort(function(a:PackId, b:PackId) return a.toPackLabel() > b.toPackLabel() ? 1 : -1);
packs.data = list;
networkButton.visible = settings.enableNetwork;
settings.signal.connect(onSettingChange);
}
override public function onHide():Void {
super.onHide();
network.stateSignal.disconnect(onConnectionState);
settings.signal.disconnect(onSettingChange);
}
private function onSettingChange(setting:Setting):Void {
switch setting {
case ENABLE_NETWORK(value): networkButton.visible = value;
case _:
}
}
private function packButtonFactory(index:Int, packId:PackId):ButtonView {
@@ -88,37 +95,6 @@ using ru.m.tankz.view.ViewUtil;
}
}
private function onConnectionState(state:ConnectionState):Void {
L.d("ClientView", 'onConnectionState: ${state}');
switch state {
case ONLINE(user):
username.text = user.name;
logoutButton.visible = true;
loginButton.visible = false;
case CONNECTED:
username.text = "";
logoutButton.visible = false;
loginButton.visible = true;
case ERROR(error):
//L.e("ClientView", 'onConnectionState: ERROR', error);
L.w("ClientView", 'onConnectionState: ERROR');
case _:
username.text = "";
logoutButton.visible = false;
loginButton.visible = false;
}
}
private function login():Void {
LoginPopup.instance.show().then(function(user:User):Void {
L.d("Login", 'user: $user');
});
}
private function logout():Void {
network.logout();
}
private function appUpdate():Void {
appUpdater.download();
}

View File

@@ -35,9 +35,8 @@ views:
visible: false
- id: network
$type: haxework.view.form.ButtonView
style: button.menu
+onPress: ~startNetwork()
text: Network
text: Network (alpha)
visible: false
- $type: haxework.view.form.LabelView
geometry.hAlign: right
@@ -55,21 +54,7 @@ views:
style: button.settings
+onPress: ~switcher.change('settings')
- $type: haxework.view.SpriteView
geometry.width: 50%
- id: username
$type: haxework.view.form.LabelView
style: text
- id: login
$type: haxework.view.form.ButtonView
style: button.login
+onPress: ~login()
- id: logout
$type: haxework.view.form.ButtonView
style: button.logout
+onPress: ~logout()
visible: false
- $type: haxework.view.SpriteView
geometry.width: 50%
geometry.width: 100%
- id: update
$type: haxework.view.form.ButtonView
style: button.active

View File

@@ -9,6 +9,7 @@ import ru.m.tankz.storage.SettingsStorage;
public static var ID(default, never):String = "settings";
@:view("fps") var fpsButton:ToggleButtonView;
@:view("network") var networkButton:ToggleButtonView;
@:provide static var switcher:FrameSwitcher;
@:provide static var settings:SettingsStorage;
@@ -20,6 +21,7 @@ import ru.m.tankz.storage.SettingsStorage;
override public function onShow(data:Dynamic):Void {
super.onShow(data);
fpsButton.on = settings.displayFPS;
networkButton.on = settings.enableNetwork;
settings.signal.connect(onSettingChange);
}
@@ -29,8 +31,8 @@ import ru.m.tankz.storage.SettingsStorage;
private function onSettingChange(setting:Setting):Void {
switch setting {
case DISPLAY_FPS(value):
fpsButton.on = value;
case DISPLAY_FPS(value): fpsButton.on = value;
case ENABLE_NETWORK(value): networkButton.on = value;
case _:
}
}

View File

@@ -24,11 +24,23 @@ views:
- id: settings1
$type: ru.m.tankz.view.settings.SettingsEditor
controlIndex: 1
- id: fps
$type: haxework.view.form.ToggleButtonView
- $type: haxework.view.group.GroupView
geometry.width: 100%
geometry.margin.top: 20
text: Display FPS
+onPress: ~function(button) settings.displayFPS = !cast(button,ToggleButtonView).on
layout:
$type: haxework.view.layout.TailLayout
hAlign: center
margin: 20
views:
- id: fps
$type: haxework.view.form.ToggleButtonView
text: Display FPS
+onPress: ~function(button) settings.displayFPS = !cast(button,ToggleButtonView).on
- id: network
$type: haxework.view.form.ToggleButtonView
geometry.margin.top: 20
text: Enable Network
+onPress: ~function(button) settings.enableNetwork = !cast(button,ToggleButtonView).on
- $type: haxework.view.group.HGroupView
style: panel
layout.margin: 10

View File

@@ -0,0 +1,62 @@
package ru.m.tankz.view.common;
import haxework.view.form.ButtonView;
import haxework.view.form.LabelView;
import haxework.view.group.HGroupView;
import haxework.view.skin.SpriteSkin;
import haxework.view.SpriteView;
import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.view.popup.LoginPopup;
@:template class NetworkStateView extends HGroupView {
@:view("state") var stateView:SpriteView;
@:view("user") var userLabel:LabelView;
@:view("login") var loginButton:ButtonView;
@:view("logout") var logoutButton:ButtonView;
@:provide static var network:NetworkManager;
public function init():Void {
network.stateSignal.connect(onConnectionChange);
}
private function onConnectionChange(state:ConnectionState):Void {
visible = true;
stateView.visible = true;
userLabel.text = "";
loginButton.visible = false;
logoutButton.visible = false;
switch state {
case OFFLINE:
cast(stateView.skin, SpriteSkin).background.color = "black";
stateView.visible = false;
visible = false;
case CONNECT:
cast(stateView.skin, SpriteSkin).background.color = "yellow";
case CONNECTED:
cast(stateView.skin, SpriteSkin).background.color = "gray";
loginButton.visible = true;
case LOGIN:
cast(stateView.skin, SpriteSkin).background.color = "yellow";
case ONLINE(user):
cast(stateView.skin, SpriteSkin).background.color = "green";
userLabel.text = user.name;
logoutButton.visible = true;
case ERROR(error):
cast(stateView.skin, SpriteSkin).background.color = "red";
userLabel.text = Std.string(error).substr(0, 10);
}
stateView.toRedraw();
}
private function login():Void {
LoginPopup.instance.show().then(function(user:User):Void {
L.d("Login", 'user: $user');
});
}
private function logout():Void {
network.logout();
}
}

View File

@@ -0,0 +1,33 @@
---
geometry.padding: [5, 2]
layout.vAlign: middle
skin:
$type: haxework.view.skin.SpriteSkin
border.color: 0x95937D
round: 10
views:
- id: state
$type: haxework.view.SpriteView
geometry.width: 20
geometry.height: 20
visible: false
skin:
$type: haxework.view.skin.SpriteSkin
background.color: black
border.color: 0xE7E0BB
round: 20
- id: user
$type: haxework.view.form.LabelView
font.size: 14
- id: login
$type: haxework.view.form.ButtonView
style: button.small
text: login
+onPress: ~login()
visible: false
- id: logout
$type: haxework.view.form.ButtonView
style: button.small
text: logout
+onPress: ~logout()
visible: false

View File

@@ -1,7 +1,6 @@
package ru.m.tankz.view.gamepad;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.MouseEvent;
import flash.events.TouchEvent;
import haxework.signal.Signal;
@@ -29,7 +28,6 @@ class GamepadView extends SpriteView implements IControlDevice {
}
private var builder:IActionAreaBuilder;
private var stage:Stage;
private var actionLayer:Sprite;
@@ -51,7 +49,6 @@ class GamepadView extends SpriteView implements IControlDevice {
private function onMouseDown(event:MouseEvent):Void {
onMouseMove(event);
stage = actionLayer.stage;
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
@@ -69,7 +66,6 @@ class GamepadView extends SpriteView implements IControlDevice {
private function onTouchBegin(event:TouchEvent):Void {
onTouchMove(event);
stage = actionLayer.stage;
stage.addEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
stage.addEventListener(TouchEvent.TOUCH_END, onTouchEnd);
}

View File

@@ -0,0 +1,46 @@
package ru.m.tankz.bundle;
import haxework.storage.IStorage;
import ru.m.tankz.bundle.ILevelBundle;
import ru.m.tankz.config.Config;
import ru.m.tankz.Type;
class CachedLevelBundle implements ILevelBundle {
private var storage:IStorage;
private var cache:Map<String, LevelPack>;
private var source:ILevelSource;
public function new(source:ILevelSource, storage:IStorage = null) {
this.storage = storage;
this.cache = new Map();
this.source = source;
}
public function load():Void {
for (packId in list()) {
get(packId);
}
}
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;
}
}
public function list():Array<PackId> {
return source.list();
}
}

View File

@@ -3,7 +3,13 @@ package ru.m.tankz.bundle;
import ru.m.tankz.config.Config;
import ru.m.tankz.Type;
interface ILevelSource {
public function resolve(id:PackId):LevelPack;
public function list():Array<PackId>;
}
interface ILevelBundle {
public function load():Void;
public function get(id:PackId):LevelPack;
public function list():Array<PackId>;
}

View File

@@ -10,7 +10,7 @@ import haxework.view.theme.ITheme;
import ru.m.tankz.bundle.ConfigBundle;
import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.bundle.ILevelBundle;
import ru.m.tankz.bundle.LevelBundle;
import ru.m.tankz.bundle.ClientLevelSource;
import ru.m.tankz.editor.view.EditorView;
import ru.m.tankz.editor.view.PackListFrame;
@@ -41,7 +41,7 @@ class Editor {
theme = new AppTheme();
Provider.setFactory(IConfigBundle, ConfigBundle);
Provider.setFactory(ILevelBundle, LevelBundle);
Provider.setFactory(ILevelBundle, ClientLevelSource);
storage = new EditorStorage();
popupManager = new PopupManager();

View File

@@ -25,7 +25,7 @@ class FileUtil {
d.throwError(event);
});
file.addEventListener(ProgressEvent.PROGRESS, function(event:ProgressEvent) {
trace('progress', '${event}');
//trace('progress', '${event}');
});
file.addEventListener(Event.COMPLETE, function(event:Event) {
var f:FileReference = cast event.target;

View File

@@ -82,7 +82,7 @@ using ru.m.tankz.view.ViewUtil;
}
private function levelViewFactory():IListItemView<LevelConfig> {
var result = new LabelListItem(function(index:Int, value:LevelConfig) return value.toLevelLabel(true));
var result = new LabelListItem(function(index:Int, value:LevelConfig) return '#${value.id}');
result.geometry.height = 48;
return result;
}

View File

@@ -2,12 +2,12 @@ package ru.m.tankz.server;
import haxe.io.Bytes;
import haxework.log.TraceLogger;
import haxework.provider.Provider;
import neko.net.ThreadServer;
import ru.m.tankz.bundle.CachedLevelBundle;
import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.bundle.ILevelBundle;
import ru.m.tankz.server.bundle.ServerConfigBundle;
import ru.m.tankz.server.bundle.ServerLevelBundle;
import ru.m.tankz.server.bundle.ServerLevelSource;
import ru.m.tankz.server.game.GameManager;
import ru.m.tankz.server.game.IGameManager;
import ru.m.tankz.server.session.GameSession;
@@ -38,6 +38,8 @@ class Server extends ThreadServer<GameSession, Bytes> {
}
@:provide static var gameManager:IGameManager;
@:provide static var configBundle:IConfigBundle;
@:provide static var levelBundle:ILevelBundle;
public static function main() {
L.push(new TraceLogger());
@@ -46,8 +48,9 @@ class Server extends ThreadServer<GameSession, Bytes> {
#end
L.d(TAG, 'Running');
L.i(TAG, 'Build: ${CompilationOption.get("build")}');
Provider.setFactory(IConfigBundle, ServerConfigBundle);
Provider.setFactory(ILevelBundle, ServerLevelBundle);
configBundle = new ServerConfigBundle();
levelBundle = new CachedLevelBundle(new ServerLevelSource());
levelBundle.load();
gameManager = new GameManager();
var host:String = Sys.args().length > 0 ? Sys.args()[0] : "0.0.0.0";
var port:Int = Sys.args().length > 1 ? Std.parseInt(Sys.args()[1]) : 5000;

View File

@@ -7,12 +7,14 @@ import ru.m.tankz.util.LevelUtil;
import sys.FileSystem;
import sys.io.File;
class ServerLevelBundle implements ILevelBundle {
class ServerLevelSource implements ILevelSource {
private static inline var PATH = "./resources/level";
public function new() {}
public function get(id:PackId):LevelPack {
var path = FileSystem.absolutePath('./level/${id}.zip');
public function resolve(id:PackId):LevelPack {
var path = FileSystem.absolutePath('${PATH}/${id}.zip');
var bytes = File.getBytes(path);
return {
id: id,
@@ -25,7 +27,7 @@ class ServerLevelBundle implements ILevelBundle {
public function list():Array<PackId> {
var result = [];
for (path in FileSystem.readDirectory("./level")) {
for (path in FileSystem.readDirectory(PATH)) {
result.push(PackId.fromString(path.split("/").pop().split(".").shift()));
}
return result;