7 Commits

Author SHA1 Message Date
347e20ab6b feat(app): add network game mode 2026-05-20 00:06:13 +03:00
9e67513897 build(npm): update gulp-haxetool 2026-05-20 00:05:30 +03:00
e41d461ba6 feat(app): update nginx image source 2026-05-20 00:03:52 +03:00
acf0706bef docs: add screenshot 2026-05-04 16:12:46 +03:00
8874f5c04f build(scripts): update build and publish scripts 2026-04-17 20:08:29 +03:00
73206d41ec 0.6.1 2026-04-17 11:51:32 +03:00
86345117f7 feat(app): update icons 2026-04-17 11:51:21 +03:00
47 changed files with 13948 additions and 13706 deletions

View File

@@ -1,21 +1,20 @@
PROJECT=puzzlez
VERSION=$(grep -m 1 'version' ./package.json | grep -oP '"version"\s*:\s*"\K\d+\.\d+.\d+')
SDK_PATH=$HOME/sdk
PUBLISH_PATH=$HOME/public/$PROJECT
BUILD_PATH=./build
TARGET_PATH=./target
KEY_STORE=<keystore.jks>
KEY_PASS=<passphrase>
# publish
REPO=https://git.shmyga.ru/api/packages/InfernalGames
PUBLISH_USER=<username>
PUBLISH_PASSWORD=<passphrase>
# docker
DOCKER_REPO=git.shmyga.ru
DOCKER_GROUP=infernalgames
DOCKER_ROOT="$DOCKER_REPO/$DOCKER_GROUP"
VERSION=$(grep -m 1 'version' ./package.json | grep -oP '"version"\s*:\s*"\K\d+\.\d+.\d+')
DOCKER_PROJECTS=(
"$PROJECT-web:web"
)
DOCKER_ARGS=(
"PROJECT_NAME=$PROJECT"
)
DOCKER_PROJECTS="$PROJECT-web:web"
DOCKER_ARGS="PROJECT_NAME=$PROJECT"

View File

@@ -1,9 +1,13 @@
# Puzzlez
# Puzzle'z
Puzzle game
![Puzzle'z](docs/Screenshot_2026-05-04_15-42-42.jpg "Puzzle'z")
## Play
https://shmyga.ru/puzzlez/html5/index.html
## Packages
## Releases
https://git.shmyga.ru/InfernalGames/-/packages/generic/puzzlez
https://git.shmyga.ru/InfernalGames/puzzlez/releases

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

View File

@@ -4,8 +4,11 @@ const Config = require("./config.json");
const packageInfo = require("./package.json");
const { System, Sdk, Haxe, Project } = require("gulp-haxetool");
const dateformat = require("dateformat");
const argv = require("yargs").argv;
const publish = require("./tasks/gulp-publish");
Project.useRuffle();
if (packageInfo.haxe) {
Haxe.VERSION = packageInfo.haxe;
}
@@ -63,6 +66,9 @@ const config = new Project.Config({
flags: ["proto_debug"],
});
const host = argv.host || "localhost";
const port = argv.port || 5000;
const app = new Project(
Project.BuildSystem.OPENFL,
[
@@ -87,6 +93,10 @@ const app = new Project(
width: 1280,
height: 768,
},
macros: [
`CompilationOption.set('host','${host}')`,
`CompilationOption.set('port',${port})`,
],
flags: ["app"],
}),
).bind(module, gulp);

27325
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "puzzlez",
"version": "0.6.0",
"version": "0.6.1",
"private": true,
"devDependencies": {
"dateformat": "^3.0.3",
@@ -9,7 +9,7 @@
"gulp-add": "0.0.2",
"gulp-clean": "^0.4.0",
"gulp-cli": "^2.2.0",
"gulp-haxetool": "^0.1.9",
"gulp-haxetool": "^0.2.1",
"yargs": "^13.2.4"
},
"haxeDependencies": {
@@ -20,7 +20,8 @@
"svg": "1.1.3",
"protohx": "0.4.6",
"yield": "3.2.2",
"formatter": "1.16.0"
"formatter": "1.16.0",
"hxWebSockets": "1.4.0"
},
"haxe": "4.2.5"
}

View File

@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -e
cd "$(dirname $(dirname "$0"))" || exit
source .env
mkdir -p "$SDK_PATH" "$PUBLISH_PATH" "$BUILD_PATH" "$TARGET_PATH" "src-gen"
docker compose run --rm --user $(id -u):$(id -g) --build --remove-orphans builder
./scripts/docker-action

View File

@@ -3,8 +3,6 @@ set -e
cd "$(dirname $(dirname "$0"))" || exit
source .env
VERSION=$(grep -m 1 'version' ./package.json | grep -oP '"version"\s*:\s*"\K\d+\.\d+\.\d+')
PACKAGES=(
"android/${PROJECT}_${VERSION}.apk"
"debian/${PROJECT}_${VERSION}_all.deb"
@@ -33,3 +31,5 @@ do
fi
fi
done
DOCKER_ACTION=publish ./scripts/docker-action

View File

@@ -4,6 +4,8 @@ import hw.app.App;
import hw.app.Const;
import hw.log.TraceLogger;
import ru.m.puzzlez.image.ImageSourceBundle;
import ru.m.puzzlez.net.Network;
import ru.m.puzzlez.net.NetworkSource;
import ru.m.puzzlez.render.part.IPartBuilder;
import ru.m.puzzlez.settings.Settings;
import ru.m.puzzlez.source.AssetImageSource;
@@ -18,12 +20,15 @@ import ru.m.update.Updater;
class PuzzlezApp {
@:provide static var updater:Updater;
@:provide static var sourceBundle:ImageSourceBundle;
@:provide static var network:Network;
public static function main() {
// ToDo: fix @:provide macro
Settings;
IPartBuilder;
GameStorage;
Network;
NetworkSource;
sourceBundle.register(new AssetImageSource());
sourceBundle.register(new FileImageSource());
sourceBundle.register(new PixabayImageSource(CompilationOption.get("PIXABAY_KEY")));
@@ -36,5 +41,6 @@ class PuzzlezApp {
app.icon = openfl.Assets.getBitmapData("resources/icon.png");
app.view = new PuzzlezAppView();
L.d("Puzzlez", "started");
network.auth();
}
}

View File

@@ -11,14 +11,16 @@ import ru.m.skin.ButtonSVGSkin;
class PuzzlezTheme extends Theme {
private static var ICONS:Map<String, String> = [
"close" => "times-circle-solid.svg",
"setting" => "cog-solid.svg",
"image" => "image-polaroid.svg",
"lock" => "lock-alt-solid.svg",
"restore" => "window-restore-solid.svg",
"compress" => "compress-solid.svg",
"expand" => "expand-solid.svg",
"spread" => "clone.svg",
"close" => "close-circle.svg",
"setting" => "cog.svg",
"image" => "image.svg",
"lock" => "lock.svg",
"lock-open" => "lock-open.svg",
"restore" => "window-restore.svg",
"compress" => "arrow-collapse-all.svg",
"expand" => "arrow-expand-all.svg",
"shuffle" => "shuffle.svg",
"spread" => "format-align-justify.svg",
];
public function new() {
@@ -41,6 +43,7 @@ class PuzzlezTheme extends Theme {
"geometry.height" => SizeValue.fromString("8h"),
"skin" => function() return new ButtonSVGSkin(),
"skin.color" => colors.light,
"skin.svgScale" => 16.0,
]));
for (key in ICONS.keys()) {
register(new Style('icon.${key}', ["skin.svg" => Assets.getText('resources/icon/${ICONS.get(key)}'),]));

View File

@@ -5,7 +5,6 @@ import hw.connect.IConnection;
import hw.signal.Signal;
import hw.storage.SharedObjectStorage;
import promhx.Promise;
import ru.m.data.IDataSource;
import ru.m.puzzlez.proto.core.User;
import ru.m.puzzlez.proto.event.GameAction;
import ru.m.puzzlez.proto.event.GameEvent;
@@ -13,6 +12,7 @@ import ru.m.puzzlez.proto.game.GamePreset;
import ru.m.puzzlez.proto.game.GameState;
import ru.m.puzzlez.proto.pack.AuthRequest;
import ru.m.puzzlez.proto.pack.GameActionRequest;
import ru.m.puzzlez.proto.pack.GameCreateRequest;
import ru.m.puzzlez.proto.pack.GameJoinRequest;
import ru.m.puzzlez.proto.pack.GameLeaveRequest;
import ru.m.puzzlez.proto.pack.GameListRequest;
@@ -21,21 +21,26 @@ import ru.m.puzzlez.proto.pack.NotificationResponse;
import ru.m.puzzlez.proto.pack.Request;
import ru.m.puzzlez.proto.pack.Response;
@:provide class Network implements IDataSource<String, GameState> {
using ru.m.SignalUtil;
@:provide class Network {
public var userSignal:Signal<User> = new Signal();
public var notificationSignal:Signal<NotificationResponse> = new Signal();
public var listSignal:Signal<GameListResponse> = new Signal();
public var joinSignal:Signal<GameState> = new Signal();
public var gameEventSignal:Signal<GameEvent> = new Signal();
private var connection:IConnection<Request, Response>;
public var connection:IConnection<Request, Response>;
private var storage:SharedObjectStorage;
private static var USER_KEY = "user";
public function new() {
storage = new SharedObjectStorage("network/2");
connection = ConnectionFactory.buildClientConnection("127.0.0.1", 5000, Response);
var host:String = CompilationOption.get("host");
var port:Int = cast(CompilationOption.get("port"), Int);
connection = ConnectionFactory.buildClientConnection(host, port, Response);
connection.handler.connect(onConnectionChange);
connection.receiveHandler.connect(onReceivePacket);
connection.connect().catchError(_ -> {});
@@ -45,17 +50,23 @@ import ru.m.puzzlez.proto.pack.Response;
if (storage.exists(USER_KEY)) {
return storage.read(USER_KEY);
} else {
return new User().setName('Anonimus #${IdUtil.generate()}');
var uuid = IdUtil.generate();
return new User().setUuid(uuid).setName('Anonimus #${uuid}');
}
}
public function auth():Promise<User> {
connection.send(new Request().setAuth(new AuthRequest().setUser(restoreUser())));
return userSignal.next();
try {
connection.send(new Request().setAuth(new AuthRequest().setUser(restoreUser())));
return userSignal.next();
} catch (error:Dynamic) {
L.w("network", "auth", error);
return Promise.promise(null);
}
}
public function createGame(preset:GamePreset):Promise<GameState> {
connection.send(new Request().setJoin(new GameJoinRequest().setPreset(preset)));
connection.send(new Request().setCreate(new GameCreateRequest().setPreset(preset)));
return joinSignal.next();
}
@@ -99,22 +110,4 @@ import ru.m.puzzlez.proto.pack.Response;
}
}
}
public function getPage(page:Page):Promise<DataPage<GameState>> {
connection.send(new Request().setList(new GameListRequest().setCount(page.count).setPage(page.index)));
return listSignal.next().then((list:GameListResponse) -> ({
page: {
index: list.page,
count: list.count,
filter: null,
order: null,
},
total: list.total,
data: list.games,
}));
}
public function get(id:String):GameState {
return null;
}
}

View File

@@ -2,10 +2,12 @@ package ru.m.puzzlez.net;
import ru.m.puzzlez.proto.event.GameStart;
import hw.signal.Signal;
import ru.m.puzzlez.image.IGame;
import ru.m.puzzlez.game.IGame;
import ru.m.puzzlez.proto.event.GameAction;
import ru.m.puzzlez.proto.event.GameEvent;
import ru.m.puzzlez.proto.game.GameState;
import ru.m.puzzlez.proto.event.gameaction.Action;
import ru.m.puzzlez.game.EventUtil;
class NetworkGame implements IGame {
public var state(default, null):GameState;
@@ -19,7 +21,11 @@ class NetworkGame implements IGame {
}
public function action(action:GameAction):Void {
network.sendGameAction(action);
if (action.action == Action.MOVE) {
events.emit(EventUtil.action(action));
} else {
network.sendGameAction(action);
}
}
public function start():Void {

View File

@@ -0,0 +1,28 @@
package ru.m.puzzlez.net;
import promhx.Promise;
import ru.m.data.DataSource;
import ru.m.puzzlez.proto.game.GameState;
import ru.m.puzzlez.proto.pack.GameListRequest;
import ru.m.puzzlez.proto.pack.GameListResponse;
import ru.m.puzzlez.proto.pack.Request;
using ru.m.SignalUtil;
@:provide class NetworkSource implements DataSource<GameState> {
@:provide private var network:Network;
public function getPage(page:Page):Promise<DataPage<GameState>> {
network.connection.send(new Request().setList(new GameListRequest().setCount(page.count).setPage(page.index)));
return network.listSignal.next().then((list:GameListResponse) -> ({
page: {
index: list.page,
count: list.count,
filter: null,
order: null,
},
total: list.total,
data: list.games,
}));
}
}

View File

@@ -1,6 +1,7 @@
package ru.m.puzzlez.settings;
import hw.storage.SharedObjectStorage;
import ru.m.puzzlez.proto.game.ImageId;
import ru.m.puzzlez.render.Background;
@:provide class Settings extends SharedObjectStorage {
@@ -10,8 +11,12 @@ import ru.m.puzzlez.render.Background;
public var background(get, set):Background;
private function defaultBackground():Background {
return Background.IMAGE(new ImageId().setSource('asset').setId('resources/texture/pool-table.png'));
}
private inline function get_background():Background {
return exists(BACKGROUND_KEY) ? read(BACKGROUND_KEY) : NONE;
return exists(BACKGROUND_KEY) ? read(BACKGROUND_KEY) : this.defaultBackground();
}
private inline function set_background(value:Background):Background {
@@ -22,7 +27,7 @@ import ru.m.puzzlez.render.Background;
public var locked(get, set):Bool;
private inline function get_locked():Bool {
return exists(LOCKED_KEY) ? read(LOCKED_KEY) : false;
return exists(LOCKED_KEY) ? read(LOCKED_KEY) : true;
}
private inline function set_locked(value:Bool):Bool {

View File

@@ -25,6 +25,7 @@ class NginxImageSource implements ImageSource {
public var id(default, never):String = "nginx";
private var baseUrl:String;
private var cache:Map<String, Promise<NginxResponse>> = new Map();
public function new(baseUrl:String) {
this.baseUrl = baseUrl;
@@ -36,31 +37,35 @@ class NginxImageSource implements ImageSource {
public function getPage(page:Page):Promise<DataPage<ImageId>> {
var category = page.filter.get("category");
return new JsonLoader<NginxResponse>().GET(category != null ? this.baseUrl + category + "/" : this.baseUrl)
.then((response:NginxResponse) -> {
var data:Array<ImageId> = [];
for (item in response) {
if (item.type == NginxResponseItemType.FILE) {
var itemId = category != null ? category + "@" + item.name : item.name;
data.push(new ImageId().setSource(id).setId(itemId));
}
var url = category != null ? this.baseUrl + category + "/" : this.baseUrl;
if (!this.cache.exists(url)) {
this.cache.set(url, new JsonLoader<NginxResponse>().GET(url));
}
return this.cache.get(url).then((response:NginxResponse) -> {
var data:Array<ImageId> = [];
for (item in response) {
if (item.type == NginxResponseItemType.FILE) {
var itemId = category != null ? category + "@" + item.name : item.name;
data.push(new ImageId().setSource(id).setId(itemId));
}
return {
page: page,
data: data,
total: response.length,
}
});
}
data = data.slice(page.index * page.count, (page.index + 1) * page.count);
return {
page: page,
data: data,
total: response.length,
}
});
}
public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
var url = this.baseUrl + StringTools.replace(id, "@", "/");
if (thumb) {
var parts = url.split(".");
var index = parts.length - 2;
parts[index] = parts[index] + "-thumbnail";
url = parts.join(".");
}
// TODO: default size by screen width?
var width = thumb ? 360 : 1920;
var parts = url.split(".");
var index = parts.length - 2;
parts[index] = parts[index] + '-w${width}';
url = parts.join(".");
return Promise.promise(ImageValue.URL(url));
}

View File

@@ -7,6 +7,7 @@ import hw.view.popup.ConfirmView;
import promhx.Promise;
import ru.m.puzzlez.game.Game;
import ru.m.puzzlez.game.IGame;
import ru.m.puzzlez.net.NetworkGame;
import ru.m.puzzlez.proto.event.GameAction;
import ru.m.puzzlez.proto.event.GameEvent;
import ru.m.puzzlez.proto.event.gameaction.Action;
@@ -37,7 +38,7 @@ import ru.m.puzzlez.view.popup.PreviewPopup;
L.d("Frame", '$ID: ${state.preset.image.source}:${state.preset.image.id}');
onHide();
if (state.online) {
// game = new NetworkGame(state);
game = new NetworkGame(state);
} else {
game = new Game(state);
}

View File

@@ -22,11 +22,13 @@ views:
settings.locked = render.manager.locked;
cast(button, hw.view.form.ToggleButtonView).on = !render.manager.locked;
}
visible: false
- $type: hw.view.form.ButtonView
style: icon.restore
+onPress: ~render.manager.reset()
visible: false
- $type: hw.view.form.ButtonView
style: icon.spread
style: icon.shuffle
+onPress: ~shuffle()
- $type: hw.view.form.ButtonView
style: icon.spread

View File

@@ -6,6 +6,7 @@ import hw.view.form.ToggleButtonView;
import hw.view.frame.FrameSwitcher;
import hw.view.frame.FrameView;
import ru.m.puzzlez.game.GameUtil;
import ru.m.puzzlez.net.Network;
import ru.m.puzzlez.proto.game.ImageId;
import ru.m.puzzlez.view.common.PresetView;
@@ -17,7 +18,7 @@ import ru.m.puzzlez.view.common.PresetView;
@:provide var switcher:FrameSwitcher;
// @:provide var network:Network;
@:provide var network:Network;
private var imageId:ImageId;
public function new() {
@@ -55,7 +56,7 @@ import ru.m.puzzlez.view.common.PresetView;
private function start(online:Bool = false):Void {
if (online) {
// network.createGame(imageView.state.preset).then(state -> switcher.change(GameFrame.ID, state));
network.createGame(imageView.state.preset).then(state -> switcher.change(GameFrame.ID, state));
} else {
switcher.change(GameFrame.ID, imageView.state);
}

View File

@@ -29,7 +29,7 @@ views:
geometry.margin.left: 15
text: Network
+onPress: ~start(true)
visible: false
visible: true
- id: image
$type: ru.m.puzzlez.view.common.PresetView
geometry.stretch: true

View File

@@ -6,6 +6,7 @@ import hw.view.frame.FrameSwitcher;
import hw.view.frame.FrameView;
import ru.m.puzzlez.FileUtil;
import ru.m.puzzlez.image.ImageSourceBundle;
import ru.m.puzzlez.net.NetworkSource;
import ru.m.puzzlez.proto.game.GameStatus;
import ru.m.puzzlez.storage.FileStorage;
import ru.m.puzzlez.storage.GameStorage;
@@ -28,6 +29,7 @@ import ru.m.update.Updater;
@:provide var fileStorage:FileStorage;
@:provide var gameStorage:GameStorage;
@:provide var sourceBundle:ImageSourceBundle;
@:provide var networkSource:NetworkSource;
private var fileSource:ImageListConfig = {title: "Files", sourceId: "file"};
private var startedGames:GameListConfig = {
@@ -40,6 +42,10 @@ import ru.m.update.Updater;
source: gameStorage,
filter: ["status" => GameStatus.COMPLETE]
};
private var networkGames:GameListConfig = {
title: "Network",
source: networkSource
};
public function new() {
super(ID);

View File

@@ -39,6 +39,10 @@ views:
$type: hw.view.form.ButtonView
text: Completed
+onPress: ~showGames(completedGames)
- id: networkButton
$type: hw.view.form.ButtonView
text: Network
+onPress: ~showGames(networkGames)
- $type: hw.view.form.ButtonView
text: Upload
geometry.margin.left: 30

View File

@@ -13,6 +13,7 @@ using hw.color.ColorUtil;
@:style(null) public var svg:String;
@:style(0) public var color:Null<Color>;
@:style(false) public var solid:Null<Bool>;
@:style(0.8) public var svgScale:Null<Float>;
private var svgs:Map<ButtonState, SVG>;
private var needUpdate:Bool;
@@ -60,7 +61,7 @@ using hw.color.ColorUtil;
graphics.lineStyle(2, color.multiply(1.5));
}
// ToDo: padding
svg.render(graphics, 0, 0, Std.int(view.width * 0.8), Std.int(view.height * 0.8));
svg.render(graphics, 0, 0, Std.int(view.width * this.svgScale), Std.int(view.height * this.svgScale));
graphics.lineStyle();
graphics.endFill();
}

View File

@@ -1 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="arrow-alt-circle-left" class="svg-inline--fa fa-arrow-alt-circle-left fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 504C119 504 8 393 8 256S119 8 256 8s248 111 248 248-111 248-248 248zm116-292H256v-70.9c0-10.7-13-16.1-20.5-8.5L121.2 247.5c-4.7 4.7-4.7 12.2 0 16.9l114.3 114.9c7.6 7.6 20.5 2.2 20.5-8.5V300h116c6.6 0 12-5.4 12-12v-64c0-6.6-5.4-12-12-12z"></path></svg>

Before

Width:  |  Height:  |  Size: 503 B

View File

@@ -1 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="arrow-alt-circle-right" class="svg-inline--fa fa-arrow-alt-circle-right fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8c137 0 248 111 248 248S393 504 256 504 8 393 8 256 119 8 256 8zM140 300h116v70.9c0 10.7 13 16.1 20.5 8.5l114.3-114.9c4.7-4.7 4.7-12.2 0-16.9l-114.3-115c-7.6-7.6-20.5-2.2-20.5 8.5V212H140c-6.6 0-12 5.4-12 12v64c0 6.6 5.4 12 12 12z"></path></svg>

Before

Width:  |  Height:  |  Size: 499 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor"
d="M19.5,3.09L20.91,4.5L16.41,9H20V11H13V4H15V7.59L19.5,3.09M20.91,19.5L19.5,20.91L15,16.41V20H13V13H20V15H16.41L20.91,19.5M4.5,3.09L9,7.59V4H11V11H4V9H7.59L3.09,4.5L4.5,3.09M3.09,19.5L7.59,15H4V13H11V20H9V16.41L4.5,20.91L3.09,19.5Z" />
</svg>

After

Width:  |  Height:  |  Size: 336 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor"
d="M9.5,13.09L10.91,14.5L6.41,19H10V21H3V14H5V17.59L9.5,13.09M10.91,9.5L9.5,10.91L5,6.41V10H3V3H10V5H6.41L10.91,9.5M14.5,13.09L19,17.59V14H21V21H14V19H17.59L13.09,14.5L14.5,13.09M13.09,9.5L17.59,5H14V3H21V10H19V6.41L14.5,10.91L13.09,9.5Z" />
</svg>

After

Width:  |  Height:  |  Size: 341 B

View File

@@ -1 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="bars" class="svg-inline--fa fa-bars fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"></path></svg>

Before

Width:  |  Height:  |  Size: 569 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 464H288c8.8 0 16-7.2 16-16V384h48v64c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h64v48H64c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16zM224 304H448c8.8 0 16-7.2 16-16V64c0-8.8-7.2-16-16-16H224c-8.8 0-16 7.2-16 16V288c0 8.8 7.2 16 16 16zm-64-16V64c0-35.3 28.7-64 64-64H448c35.3 0 64 28.7 64 64V288c0 35.3-28.7 64-64 64H224c-35.3 0-64-28.7-64-64z"/></svg>

Before

Width:  |  Height:  |  Size: 604 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor"
d="M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z" />
</svg>

After

Width:  |  Height:  |  Size: 307 B

View File

@@ -1 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="cog" class="svg-inline--fa fa-cog fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor"
d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z" />
</svg>

After

Width:  |  Height:  |  Size: 1014 B

View File

@@ -1 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="compress" class="svg-inline--fa fa-compress fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M436 192H312c-13.3 0-24-10.7-24-24V44c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v84h84c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12zm-276-24V44c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v84H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24zm0 300V344c0-13.3-10.7-24-24-24H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12zm192 0v-84h84c6.6 0 12-5.4 12-12v-40c0-6.6-5.4-12-12-12H312c-13.3 0-24 10.7-24 24v124c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12z"></path></svg>

Before

Width:  |  Height:  |  Size: 741 B

View File

@@ -1 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="expand" class="svg-inline--fa fa-expand fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M0 180V56c0-13.3 10.7-24 24-24h124c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12H64v84c0 6.6-5.4 12-12 12H12c-6.6 0-12-5.4-12-12zM288 44v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12V56c0-13.3-10.7-24-24-24H300c-6.6 0-12 5.4-12 12zm148 276h-40c-6.6 0-12 5.4-12 12v84h-84c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24V332c0-6.6-5.4-12-12-12zM160 468v-40c0-6.6-5.4-12-12-12H64v-84c0-6.6-5.4-12-12-12H12c-6.6 0-12 5.4-12 12v124c0 13.3 10.7 24 24 24h124c6.6 0 12-5.4 12-12z"></path></svg>

Before

Width:  |  Height:  |  Size: 740 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor" d="M3,3H21V5H3V3M3,7H21V9H3V7M3,11H21V13H3V11M3,15H21V17H3V15M3,19H21V21H3V19Z" />
</svg>

After

Width:  |  Height:  |  Size: 178 B

View File

@@ -1,6 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="image-polaroid" role="img"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-image-polaroid fa-w-14 fa-2x">
<path fill="currentColor"
d="M128 192a32 32 0 1 0-32-32 32 32 0 0 0 32 32zM416 32H32A32 32 0 0 0 0 64v384a32 32 0 0 0 32 32h384a32 32 0 0 0 32-32V64a32 32 0 0 0-32-32zm-32 320H64V96h320zM268.8 209.07a16 16 0 0 0-25.6 0l-49.32 65.75L173.31 244a16 16 0 0 0-26.62 0L96 320h256z"
class=""></path>
</svg>

Before

Width:  |  Height:  |  Size: 540 B

View File

@@ -1 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="image" class="svg-inline--fa fa-image fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M464 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h416c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48zM112 120c-30.928 0-56 25.072-56 56s25.072 56 56 56 56-25.072 56-56-25.072-56-56-56zM64 384h384V272l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L208 320l-55.515-55.515c-4.686-4.686-12.284-4.686-16.971 0L64 336v48z"></path></svg>

Before

Width:  |  Height:  |  Size: 564 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor"
d="M8.5,13.5L11,16.5L14.5,12L19,18H5M21,19V5C21,3.89 20.1,3 19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19Z" />
</svg>

After

Width:  |  Height:  |  Size: 222 B

View File

@@ -1,6 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="lock-alt" role="img"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-lock-alt fa-w-14 fa-2x">
<path fill="currentColor"
d="M400 224h-24v-72C376 68.2 307.8 0 224 0S72 68.2 72 152v72H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48zM264 392c0 22.1-17.9 40-40 40s-40-17.9-40-40v-48c0-22.1 17.9-40 40-40s40 17.9 40 40v48zm32-168H152v-72c0-39.7 32.3-72 72-72s72 32.3 72 72v72z"
class=""></path>
</svg>

Before

Width:  |  Height:  |  Size: 582 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor"
d="M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10A2,2 0 0,1 6,8H15V6A3,3 0 0,0 12,3A3,3 0 0,0 9,6H7A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,17A2,2 0 0,0 14,15A2,2 0 0,0 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17Z" />
</svg>

After

Width:  |  Height:  |  Size: 328 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor"
d="M12,17A2,2 0 0,0 14,15C14,13.89 13.1,13 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V10C4,8.89 4.9,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z" />
</svg>

After

Width:  |  Height:  |  Size: 342 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor"
d="M14.83,13.41L13.42,14.82L16.55,17.95L14.5,20H20V14.5L17.96,16.54L14.83,13.41M14.5,4L16.54,6.04L4,18.59L5.41,20L17.96,7.46L20,9.5V4M10.59,9.17L5.41,4L4,5.41L9.17,10.58L10.59,9.17Z" />
</svg>

After

Width:  |  Height:  |  Size: 285 B

View File

@@ -1 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="times-circle" class="svg-inline--fa fa-times-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"></path></svg>

Before

Width:  |  Height:  |  Size: 619 B

View File

@@ -1 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="window-close" class="svg-inline--fa fa-window-close fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M464 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h416c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-83.6 290.5c4.8 4.8 4.8 12.6 0 17.4l-40.5 40.5c-4.8 4.8-12.6 4.8-17.4 0L256 313.3l-66.5 67.1c-4.8 4.8-12.6 4.8-17.4 0l-40.5-40.5c-4.8-4.8-4.8-12.6 0-17.4l67.1-66.5-67.1-66.5c-4.8-4.8-4.8-12.6 0-17.4l40.5-40.5c4.8-4.8 12.6-4.8 17.4 0l66.5 67.1 66.5-67.1c4.8-4.8 12.6-4.8 17.4 0l40.5 40.5c4.8 4.8 4.8 12.6 0 17.4L313.3 256l67.1 66.5z"></path></svg>

Before

Width:  |  Height:  |  Size: 681 B

View File

@@ -1,6 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="window-restore" role="img"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-window-restore fa-w-16 fa-2x">
<path fill="currentColor"
d="M512 48v288c0 26.5-21.5 48-48 48h-48V176c0-44.1-35.9-80-80-80H128V48c0-26.5 21.5-48 48-48h288c26.5 0 48 21.5 48 48zM384 176v288c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V176c0-26.5 21.5-48 48-48h288c26.5 0 48 21.5 48 48zm-68 28c0-6.6-5.4-12-12-12H76c-6.6 0-12 5.4-12 12v52h252v-52z"
class=""></path>
</svg>

Before

Width:  |  Height:  |  Size: 580 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor" d="M4,8H8V4H20V16H16V20H4V8M16,8V14H18V6H10V8H16M6,12V18H14V12H6Z" />
</svg>

After

Width:  |  Height:  |  Size: 165 B

View File

@@ -0,0 +1,18 @@
package ru.m;
import hw.signal.Signal;
import promhx.Deferred;
import promhx.Promise;
class SignalUtil {
public static function next<T>(signal:Signal<T>):Promise<T> {
var d:Deferred<T> = new Deferred<T>();
var receiver:T->Void;
receiver = (value:T) -> {
signal.disconnect(receiver);
d.resolve(value);
};
signal.connect(receiver);
return d.promise();
}
}

View File

@@ -5,8 +5,8 @@ import ru.m.puzzlez.proto.pack.GameListResponse;
import ru.m.puzzlez.proto.pack.GameListRequest;
import hw.connect.session.ProtoSession;
import hw.log.BaseLogger.LoggerUtil;
import ru.m.puzzlez.image.Game;
import ru.m.puzzlez.image.GameUtil;
import ru.m.puzzlez.game.Game;
import ru.m.puzzlez.game.GameUtil;
import ru.m.puzzlez.proto.core.User;
import ru.m.puzzlez.proto.event.GameAction;
import ru.m.puzzlez.proto.event.GameEvent;