parent
35101b5cec
commit
0bc36b69b9
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20240508185559 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE "character" (id UUID NOT NULL, dojo_id UUID DEFAULT NULL, name VARCHAR(64) NOT NULL, strength INT NOT NULL, constitution INT NOT NULL, agility INT NOT NULL, chi INT NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_937AB03432F09E9C ON "character" (dojo_id)');
|
||||
$this->addSql('COMMENT ON COLUMN "character".id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN "character".dojo_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE character_technique (character_id UUID NOT NULL, technique_id UUID NOT NULL, PRIMARY KEY(character_id, technique_id))');
|
||||
$this->addSql('CREATE INDEX IDX_506B3A7A1136BE75 ON character_technique (character_id)');
|
||||
$this->addSql('CREATE INDEX IDX_506B3A7A1F8ACB26 ON character_technique (technique_id)');
|
||||
$this->addSql('COMMENT ON COLUMN character_technique.character_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN character_technique.technique_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE city (id UUID NOT NULL, dungeon_id UUID NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_2D5B0234B606863 ON city (dungeon_id)');
|
||||
$this->addSql('COMMENT ON COLUMN city.id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN city.dungeon_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE country (id UUID NOT NULL, capital_id UUID NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_5373C966FC2D9FF7 ON country (capital_id)');
|
||||
$this->addSql('COMMENT ON COLUMN country.id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN country.capital_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE dojo (id UUID NOT NULL, village_id UUID DEFAULT NULL, owner_id UUID NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_9494CCB15E237E06 ON dojo (name)');
|
||||
$this->addSql('CREATE INDEX IDX_9494CCB15E0D5582 ON dojo (village_id)');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_9494CCB17E3C61F9 ON dojo (owner_id)');
|
||||
$this->addSql('COMMENT ON COLUMN dojo.id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN dojo.village_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN dojo.owner_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE dungeon (id UUID NOT NULL, city_id UUID NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_3FFA1F908BAC62AF ON dungeon (city_id)');
|
||||
$this->addSql('COMMENT ON COLUMN dungeon.id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN dungeon.city_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE fight (id UUID NOT NULL, winner_id UUID DEFAULT NULL, tournament_id UUID NOT NULL, start_date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, events JSON NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_21AA44565DFCD4B8 ON fight (winner_id)');
|
||||
$this->addSql('CREATE INDEX IDX_21AA445633D1A3E7 ON fight (tournament_id)');
|
||||
$this->addSql('COMMENT ON COLUMN fight.id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN fight.winner_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN fight.tournament_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE fight_character (fight_id UUID NOT NULL, character_id UUID NOT NULL, PRIMARY KEY(fight_id, character_id))');
|
||||
$this->addSql('CREATE INDEX IDX_BBD57C41AC6657E4 ON fight_character (fight_id)');
|
||||
$this->addSql('CREATE INDEX IDX_BBD57C411136BE75 ON fight_character (character_id)');
|
||||
$this->addSql('COMMENT ON COLUMN fight_character.fight_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN fight_character.character_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE prefecture (id UUID NOT NULL, capital_id UUID NOT NULL, country_id UUID NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_ABE6511AFC2D9FF7 ON prefecture (capital_id)');
|
||||
$this->addSql('CREATE INDEX IDX_ABE6511AF92F3E70 ON prefecture (country_id)');
|
||||
$this->addSql('COMMENT ON COLUMN prefecture.id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN prefecture.capital_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN prefecture.country_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE technique (id UUID NOT NULL, prerequisite_id UUID DEFAULT NULL, name VARCHAR(255) NOT NULL, costs INT NOT NULL, damage VARCHAR(255) NOT NULL, energy VARCHAR(255) NOT NULL, accuracy VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_D73B98415E237E06 ON technique (name)');
|
||||
$this->addSql('CREATE INDEX IDX_D73B9841276AF86B ON technique (prerequisite_id)');
|
||||
$this->addSql('COMMENT ON COLUMN technique.id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN technique.prerequisite_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE tournament (id UUID NOT NULL, winner_id UUID DEFAULT NULL, name VARCHAR(255) NOT NULL, start_date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_BD5FB8D95DFCD4B8 ON tournament (winner_id)');
|
||||
$this->addSql('COMMENT ON COLUMN tournament.id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN tournament.winner_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE tournament_character (tournament_id UUID NOT NULL, character_id UUID NOT NULL, PRIMARY KEY(tournament_id, character_id))');
|
||||
$this->addSql('CREATE INDEX IDX_43FA182533D1A3E7 ON tournament_character (tournament_id)');
|
||||
$this->addSql('CREATE INDEX IDX_43FA18251136BE75 ON tournament_character (character_id)');
|
||||
$this->addSql('COMMENT ON COLUMN tournament_character.tournament_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN tournament_character.character_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE "user" (id UUID NOT NULL, dojo_id UUID DEFAULT NULL, auth_name VARCHAR(32) NOT NULL, properties JSON NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D64932F09E9C ON "user" (dojo_id)');
|
||||
$this->addSql('COMMENT ON COLUMN "user".id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN "user".dojo_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('CREATE TABLE village (id UUID NOT NULL, prefecture_id UUID NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_4E6C7FAA9D39C865 ON village (prefecture_id)');
|
||||
$this->addSql('COMMENT ON COLUMN village.id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN village.prefecture_id IS \'(DC2Type:ulid)\'');
|
||||
$this->addSql('ALTER TABLE "character" ADD CONSTRAINT FK_937AB03432F09E9C FOREIGN KEY (dojo_id) REFERENCES dojo (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE character_technique ADD CONSTRAINT FK_506B3A7A1136BE75 FOREIGN KEY (character_id) REFERENCES "character" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE character_technique ADD CONSTRAINT FK_506B3A7A1F8ACB26 FOREIGN KEY (technique_id) REFERENCES technique (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE city ADD CONSTRAINT FK_2D5B0234B606863 FOREIGN KEY (dungeon_id) REFERENCES dungeon (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE country ADD CONSTRAINT FK_5373C966FC2D9FF7 FOREIGN KEY (capital_id) REFERENCES city (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE dojo ADD CONSTRAINT FK_9494CCB15E0D5582 FOREIGN KEY (village_id) REFERENCES village (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE dojo ADD CONSTRAINT FK_9494CCB17E3C61F9 FOREIGN KEY (owner_id) REFERENCES "user" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE dungeon ADD CONSTRAINT FK_3FFA1F908BAC62AF FOREIGN KEY (city_id) REFERENCES city (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE fight ADD CONSTRAINT FK_21AA44565DFCD4B8 FOREIGN KEY (winner_id) REFERENCES "character" (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE fight ADD CONSTRAINT FK_21AA445633D1A3E7 FOREIGN KEY (tournament_id) REFERENCES tournament (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE fight_character ADD CONSTRAINT FK_BBD57C41AC6657E4 FOREIGN KEY (fight_id) REFERENCES fight (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE fight_character ADD CONSTRAINT FK_BBD57C411136BE75 FOREIGN KEY (character_id) REFERENCES "character" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE prefecture ADD CONSTRAINT FK_ABE6511AFC2D9FF7 FOREIGN KEY (capital_id) REFERENCES city (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE prefecture ADD CONSTRAINT FK_ABE6511AF92F3E70 FOREIGN KEY (country_id) REFERENCES country (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE technique ADD CONSTRAINT FK_D73B9841276AF86B FOREIGN KEY (prerequisite_id) REFERENCES technique (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE tournament ADD CONSTRAINT FK_BD5FB8D95DFCD4B8 FOREIGN KEY (winner_id) REFERENCES "character" (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE tournament_character ADD CONSTRAINT FK_43FA182533D1A3E7 FOREIGN KEY (tournament_id) REFERENCES tournament (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE tournament_character ADD CONSTRAINT FK_43FA18251136BE75 FOREIGN KEY (character_id) REFERENCES "character" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE "user" ADD CONSTRAINT FK_8D93D64932F09E9C FOREIGN KEY (dojo_id) REFERENCES dojo (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE village ADD CONSTRAINT FK_4E6C7FAA9D39C865 FOREIGN KEY (prefecture_id) REFERENCES prefecture (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE "character" DROP CONSTRAINT FK_937AB03432F09E9C');
|
||||
$this->addSql('ALTER TABLE character_technique DROP CONSTRAINT FK_506B3A7A1136BE75');
|
||||
$this->addSql('ALTER TABLE character_technique DROP CONSTRAINT FK_506B3A7A1F8ACB26');
|
||||
$this->addSql('ALTER TABLE city DROP CONSTRAINT FK_2D5B0234B606863');
|
||||
$this->addSql('ALTER TABLE country DROP CONSTRAINT FK_5373C966FC2D9FF7');
|
||||
$this->addSql('ALTER TABLE dojo DROP CONSTRAINT FK_9494CCB15E0D5582');
|
||||
$this->addSql('ALTER TABLE dojo DROP CONSTRAINT FK_9494CCB17E3C61F9');
|
||||
$this->addSql('ALTER TABLE dungeon DROP CONSTRAINT FK_3FFA1F908BAC62AF');
|
||||
$this->addSql('ALTER TABLE fight DROP CONSTRAINT FK_21AA44565DFCD4B8');
|
||||
$this->addSql('ALTER TABLE fight DROP CONSTRAINT FK_21AA445633D1A3E7');
|
||||
$this->addSql('ALTER TABLE fight_character DROP CONSTRAINT FK_BBD57C41AC6657E4');
|
||||
$this->addSql('ALTER TABLE fight_character DROP CONSTRAINT FK_BBD57C411136BE75');
|
||||
$this->addSql('ALTER TABLE prefecture DROP CONSTRAINT FK_ABE6511AFC2D9FF7');
|
||||
$this->addSql('ALTER TABLE prefecture DROP CONSTRAINT FK_ABE6511AF92F3E70');
|
||||
$this->addSql('ALTER TABLE technique DROP CONSTRAINT FK_D73B9841276AF86B');
|
||||
$this->addSql('ALTER TABLE tournament DROP CONSTRAINT FK_BD5FB8D95DFCD4B8');
|
||||
$this->addSql('ALTER TABLE tournament_character DROP CONSTRAINT FK_43FA182533D1A3E7');
|
||||
$this->addSql('ALTER TABLE tournament_character DROP CONSTRAINT FK_43FA18251136BE75');
|
||||
$this->addSql('ALTER TABLE "user" DROP CONSTRAINT FK_8D93D64932F09E9C');
|
||||
$this->addSql('ALTER TABLE village DROP CONSTRAINT FK_4E6C7FAA9D39C865');
|
||||
$this->addSql('DROP TABLE "character"');
|
||||
$this->addSql('DROP TABLE character_technique');
|
||||
$this->addSql('DROP TABLE city');
|
||||
$this->addSql('DROP TABLE country');
|
||||
$this->addSql('DROP TABLE dojo');
|
||||
$this->addSql('DROP TABLE dungeon');
|
||||
$this->addSql('DROP TABLE fight');
|
||||
$this->addSql('DROP TABLE fight_character');
|
||||
$this->addSql('DROP TABLE prefecture');
|
||||
$this->addSql('DROP TABLE technique');
|
||||
$this->addSql('DROP TABLE tournament');
|
||||
$this->addSql('DROP TABLE tournament_character');
|
||||
$this->addSql('DROP TABLE "user"');
|
||||
$this->addSql('DROP TABLE village');
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
# Use the front controller as index file. It serves as a fallback solution when
|
||||
# every other rewrite/redirect fails (e.g. in an aliased environment without
|
||||
# mod_rewrite). Additionally, this reduces the matching process for the
|
||||
# start page (path "/") because otherwise Apache will apply the rewriting rules
|
||||
# to each configured DirectoryIndex file (e.g. index.php, index.html, index.pl).
|
||||
DirectoryIndex index.php
|
||||
|
||||
# By default, Apache does not evaluate symbolic links if you did not enable this
|
||||
# feature in your server configuration. Uncomment the following line if you
|
||||
# install assets as symlinks or if you experience problems related to symlinks
|
||||
# when compiling LESS/Sass/CoffeScript assets.
|
||||
# Options +SymLinksIfOwnerMatch
|
||||
|
||||
# Disabling MultiViews prevents unwanted negotiation, e.g. "/index" should not resolve
|
||||
# to the front controller "/index.php" but be rewritten to "/index.php/index".
|
||||
<IfModule mod_negotiation.c>
|
||||
Options -MultiViews
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
# This Option needs to be enabled for RewriteRule, otherwise it will show an error like
|
||||
# 'Options FollowSymLinks or SymLinksIfOwnerMatch is off which implies that RewriteRule directive is forbidden'
|
||||
Options +SymLinksIfOwnerMatch
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Determine the RewriteBase automatically and set it as environment variable.
|
||||
# If you are using Apache aliases to do mass virtual hosting or installed the
|
||||
# project in a subdirectory, the base path will be prepended to allow proper
|
||||
# resolution of the index.php file and to redirect to the correct URI. It will
|
||||
# work in environments without path prefix as well, providing a safe, one-size
|
||||
# fits all solution. But as you do not need it in this case, you can comment
|
||||
# the following 2 lines to eliminate the overhead.
|
||||
RewriteCond %{REQUEST_URI}::$0 ^(/.+)/(.*)::\2$
|
||||
RewriteRule .* - [E=BASE:%1]
|
||||
|
||||
# Sets the HTTP_AUTHORIZATION header removed by Apache
|
||||
RewriteCond %{HTTP:Authorization} .+
|
||||
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
|
||||
|
||||
# Redirect to URI without front controller to prevent duplicate content
|
||||
# (with and without `/index.php`). Only do this redirect on the initial
|
||||
# rewrite by Apache and not on subsequent cycles. Otherwise we would get an
|
||||
# endless redirect loop (request -> rewrite to front controller ->
|
||||
# redirect -> request -> ...).
|
||||
# So in case you get a "too many redirects" error or you always get redirected
|
||||
# to the start page because your Apache does not expose the REDIRECT_STATUS
|
||||
# environment variable, you have 2 choices:
|
||||
# - disable this feature by commenting the following 2 lines or
|
||||
# - use Apache >= 2.3.9 and replace all L flags by END flags and remove the
|
||||
# following RewriteCond (best solution)
|
||||
RewriteCond %{ENV:REDIRECT_STATUS} =""
|
||||
RewriteRule ^index\.php(?:/(.*)|$) %{ENV:BASE}/$1 [R=301,L]
|
||||
|
||||
# If the requested filename exists, simply serve it.
|
||||
# We only want to let Apache serve files and not directories.
|
||||
# Rewrite all other queries to the front controller.
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ %{ENV:BASE}/index.php [L]
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
<IfModule mod_alias.c>
|
||||
# When mod_rewrite is not available, we instruct a temporary redirect of
|
||||
# the start page to the front controller explicitly so that the website
|
||||
# and the generated links can still be used.
|
||||
RedirectMatch 307 ^/$ /index.php/
|
||||
# RedirectTemp cannot be used instead
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
|
||||
Header set Cross-Origin-Embedder-Policy "require-corp"
|
||||
Header set Cross-Origin-Opener-Policy "same-origin"
|
||||
|
||||
|
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,213 @@
|
||||
/**************************************************************************/
|
||||
/* audio.worklet.js */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
class RingBuffer {
|
||||
constructor(p_buffer, p_state, p_threads) {
|
||||
this.buffer = p_buffer;
|
||||
this.avail = p_state;
|
||||
this.threads = p_threads;
|
||||
this.rpos = 0;
|
||||
this.wpos = 0;
|
||||
}
|
||||
|
||||
data_left() {
|
||||
return this.threads ? Atomics.load(this.avail, 0) : this.avail;
|
||||
}
|
||||
|
||||
space_left() {
|
||||
return this.buffer.length - this.data_left();
|
||||
}
|
||||
|
||||
read(output) {
|
||||
const size = this.buffer.length;
|
||||
let from = 0;
|
||||
let to_write = output.length;
|
||||
if (this.rpos + to_write > size) {
|
||||
const high = size - this.rpos;
|
||||
output.set(this.buffer.subarray(this.rpos, size));
|
||||
from = high;
|
||||
to_write -= high;
|
||||
this.rpos = 0;
|
||||
}
|
||||
if (to_write) {
|
||||
output.set(this.buffer.subarray(this.rpos, this.rpos + to_write), from);
|
||||
}
|
||||
this.rpos += to_write;
|
||||
if (this.threads) {
|
||||
Atomics.add(this.avail, 0, -output.length);
|
||||
Atomics.notify(this.avail, 0);
|
||||
} else {
|
||||
this.avail -= output.length;
|
||||
}
|
||||
}
|
||||
|
||||
write(p_buffer) {
|
||||
const to_write = p_buffer.length;
|
||||
const mw = this.buffer.length - this.wpos;
|
||||
if (mw >= to_write) {
|
||||
this.buffer.set(p_buffer, this.wpos);
|
||||
this.wpos += to_write;
|
||||
if (mw === to_write) {
|
||||
this.wpos = 0;
|
||||
}
|
||||
} else {
|
||||
const high = p_buffer.subarray(0, mw);
|
||||
const low = p_buffer.subarray(mw);
|
||||
this.buffer.set(high, this.wpos);
|
||||
this.buffer.set(low);
|
||||
this.wpos = low.length;
|
||||
}
|
||||
if (this.threads) {
|
||||
Atomics.add(this.avail, 0, to_write);
|
||||
Atomics.notify(this.avail, 0);
|
||||
} else {
|
||||
this.avail += to_write;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GodotProcessor extends AudioWorkletProcessor {
|
||||
constructor() {
|
||||
super();
|
||||
this.threads = false;
|
||||
this.running = true;
|
||||
this.lock = null;
|
||||
this.notifier = null;
|
||||
this.output = null;
|
||||
this.output_buffer = new Float32Array();
|
||||
this.input = null;
|
||||
this.input_buffer = new Float32Array();
|
||||
this.port.onmessage = (event) => {
|
||||
const cmd = event.data['cmd'];
|
||||
const data = event.data['data'];
|
||||
this.parse_message(cmd, data);
|
||||
};
|
||||
}
|
||||
|
||||
process_notify() {
|
||||
if (this.notifier) {
|
||||
Atomics.add(this.notifier, 0, 1);
|
||||
Atomics.notify(this.notifier, 0);
|
||||
}
|
||||
}
|
||||
|
||||
parse_message(p_cmd, p_data) {
|
||||
if (p_cmd === 'start' && p_data) {
|
||||
const state = p_data[0];
|
||||
let idx = 0;
|
||||
this.threads = true;
|
||||
this.lock = state.subarray(idx, ++idx);
|
||||
this.notifier = state.subarray(idx, ++idx);
|
||||
const avail_in = state.subarray(idx, ++idx);
|
||||
const avail_out = state.subarray(idx, ++idx);
|
||||
this.input = new RingBuffer(p_data[1], avail_in, true);
|
||||
this.output = new RingBuffer(p_data[2], avail_out, true);
|
||||
} else if (p_cmd === 'stop') {
|
||||
this.running = false;
|
||||
this.output = null;
|
||||
this.input = null;
|
||||
this.lock = null;
|
||||
this.notifier = null;
|
||||
} else if (p_cmd === 'start_nothreads') {
|
||||
this.output = new RingBuffer(p_data[0], p_data[0].length, false);
|
||||
} else if (p_cmd === 'chunk') {
|
||||
this.output.write(p_data);
|
||||
}
|
||||
}
|
||||
|
||||
static array_has_data(arr) {
|
||||
return arr.length && arr[0].length && arr[0][0].length;
|
||||
}
|
||||
|
||||
process(inputs, outputs, parameters) {
|
||||
if (!this.running) {
|
||||
return false; // Stop processing.
|
||||
}
|
||||
if (this.output === null) {
|
||||
return true; // Not ready yet, keep processing.
|
||||
}
|
||||
const process_input = GodotProcessor.array_has_data(inputs);
|
||||
if (process_input) {
|
||||
const input = inputs[0];
|
||||
const chunk = input[0].length * input.length;
|
||||
if (this.input_buffer.length !== chunk) {
|
||||
this.input_buffer = new Float32Array(chunk);
|
||||
}
|
||||
if (!this.threads) {
|
||||
GodotProcessor.write_input(this.input_buffer, input);
|
||||
this.port.postMessage({ 'cmd': 'input', 'data': this.input_buffer });
|
||||
} else if (this.input.space_left() >= chunk) {
|
||||
GodotProcessor.write_input(this.input_buffer, input);
|
||||
this.input.write(this.input_buffer);
|
||||
} else {
|
||||
this.port.postMessage('Input buffer is full! Skipping input frame.');
|
||||
}
|
||||
}
|
||||
const process_output = GodotProcessor.array_has_data(outputs);
|
||||
if (process_output) {
|
||||
const output = outputs[0];
|
||||
const chunk = output[0].length * output.length;
|
||||
if (this.output_buffer.length !== chunk) {
|
||||
this.output_buffer = new Float32Array(chunk);
|
||||
}
|
||||
if (this.output.data_left() >= chunk) {
|
||||
this.output.read(this.output_buffer);
|
||||
GodotProcessor.write_output(output, this.output_buffer);
|
||||
if (!this.threads) {
|
||||
this.port.postMessage({ 'cmd': 'read', 'data': chunk });
|
||||
}
|
||||
} else {
|
||||
this.port.postMessage('Output buffer has not enough frames! Skipping output frame.');
|
||||
}
|
||||
}
|
||||
this.process_notify();
|
||||
return true;
|
||||
}
|
||||
|
||||
static write_output(dest, source) {
|
||||
const channels = dest.length;
|
||||
for (let ch = 0; ch < channels; ch++) {
|
||||
for (let sample = 0; sample < dest[ch].length; sample++) {
|
||||
dest[ch][sample] = source[sample * channels + ch];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static write_input(dest, source) {
|
||||
const channels = source.length;
|
||||
for (let ch = 0; ch < channels; ch++) {
|
||||
for (let sample = 0; sample < source[ch].length; sample++) {
|
||||
dest[sample * channels + ch] = source[ch][sample];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerProcessor('godot-processor', GodotProcessor);
|
After Width: | Height: | Size: 5.5 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 316 KiB |
@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2015 The Emscripten Authors
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
// Pthread Web Worker startup routine:
|
||||
// This is the entry point file that is loaded first by each Web Worker
|
||||
// that executes pthreads on the Emscripten application.
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
|
||||
// Thread-local guard variable for one-time init of the JS state
|
||||
var initializedJS = false;
|
||||
|
||||
function assert(condition, text) {
|
||||
if (!condition) abort('Assertion failed: ' + text);
|
||||
}
|
||||
|
||||
function threadPrintErr() {
|
||||
var text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.error(text);
|
||||
}
|
||||
function threadAlert() {
|
||||
var text = Array.prototype.slice.call(arguments).join(' ');
|
||||
postMessage({cmd: 'alert', text: text, threadId: Module['_pthread_self']()});
|
||||
}
|
||||
// We don't need out() for now, but may need to add it if we want to use it
|
||||
// here. Or, if this code all moves into the main JS, that problem will go
|
||||
// away. (For now, adding it here increases code size for no benefit.)
|
||||
var out = () => { throw 'out() is not defined in worker.js.'; }
|
||||
var err = threadPrintErr;
|
||||
self.alert = threadAlert;
|
||||
|
||||
Module['instantiateWasm'] = (info, receiveInstance) => {
|
||||
// Instantiate from the module posted from the main thread.
|
||||
// We can just use sync instantiation in the worker.
|
||||
var module = Module['wasmModule'];
|
||||
// We don't need the module anymore; new threads will be spawned from the main thread.
|
||||
Module['wasmModule'] = null;
|
||||
var instance = new WebAssembly.Instance(module, info);
|
||||
// TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193,
|
||||
// the above line no longer optimizes out down to the following line.
|
||||
// When the regression is fixed, we can remove this if/else.
|
||||
return receiveInstance(instance);
|
||||
}
|
||||
|
||||
// Turn unhandled rejected promises into errors so that the main thread will be
|
||||
// notified about them.
|
||||
self.onunhandledrejection = (e) => {
|
||||
throw e.reason ?? e;
|
||||
};
|
||||
|
||||
function handleMessage(e) {
|
||||
try {
|
||||
if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code.
|
||||
|
||||
// Until we initialize the runtime, queue up any further incoming messages.
|
||||
let messageQueue = [];
|
||||
self.onmessage = (e) => messageQueue.push(e);
|
||||
|
||||
// And add a callback for when the runtime is initialized.
|
||||
self.startWorker = (instance) => {
|
||||
Module = instance;
|
||||
// Notify the main thread that this thread has loaded.
|
||||
postMessage({ 'cmd': 'loaded' });
|
||||
// Process any messages that were queued before the thread was ready.
|
||||
for (let msg of messageQueue) {
|
||||
handleMessage(msg);
|
||||
}
|
||||
// Restore the real message handler.
|
||||
self.onmessage = handleMessage;
|
||||
};
|
||||
|
||||
// Module and memory were sent from main thread
|
||||
Module['wasmModule'] = e.data.wasmModule;
|
||||
|
||||
// Use `const` here to ensure that the variable is scoped only to
|
||||
// that iteration, allowing safe reference from a closure.
|
||||
for (const handler of e.data.handlers) {
|
||||
Module[handler] = function() {
|
||||
postMessage({ cmd: 'callHandler', handler, args: [...arguments] });
|
||||
}
|
||||
}
|
||||
|
||||
Module['wasmMemory'] = e.data.wasmMemory;
|
||||
|
||||
Module['buffer'] = Module['wasmMemory'].buffer;
|
||||
|
||||
Module['workerID'] = e.data.workerID;
|
||||
|
||||
Module['ENVIRONMENT_IS_PTHREAD'] = true;
|
||||
|
||||
if (typeof e.data.urlOrBlob == 'string') {
|
||||
importScripts(e.data.urlOrBlob);
|
||||
} else {
|
||||
var objectUrl = URL.createObjectURL(e.data.urlOrBlob);
|
||||
importScripts(objectUrl);
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
}
|
||||
Godot(Module);
|
||||
} else if (e.data.cmd === 'run') {
|
||||
// Pass the thread address to wasm to store it for fast access.
|
||||
Module['__emscripten_thread_init'](e.data.pthread_ptr, /*isMainBrowserThread=*/0, /*isMainRuntimeThread=*/0, /*canBlock=*/1);
|
||||
|
||||
// Await mailbox notifications with `Atomics.waitAsync` so we can start
|
||||
// using the fast `Atomics.notify` notification path.
|
||||
Module['__emscripten_thread_mailbox_await'](e.data.pthread_ptr);
|
||||
|
||||
assert(e.data.pthread_ptr);
|
||||
// Also call inside JS module to set up the stack frame for this pthread in JS module scope
|
||||
Module['establishStackSpace']();
|
||||
Module['PThread'].receiveObjectTransfer(e.data);
|
||||
Module['PThread'].threadInitTLS();
|
||||
|
||||
if (!initializedJS) {
|
||||
initializedJS = true;
|
||||
}
|
||||
|
||||
try {
|
||||
Module['invokeEntryPoint'](e.data.start_routine, e.data.arg);
|
||||
} catch(ex) {
|
||||
if (ex != 'unwind') {
|
||||
// The pthread "crashed". Do not call `_emscripten_thread_exit` (which
|
||||
// would make this thread joinable). Instead, re-throw the exception
|
||||
// and let the top level handler propagate it back to the main thread.
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
} else if (e.data.cmd === 'cancel') { // Main thread is asking for a pthread_cancel() on this thread.
|
||||
if (Module['_pthread_self']()) {
|
||||
Module['__emscripten_thread_exit'](-1);
|
||||
}
|
||||
} else if (e.data.target === 'setimmediate') {
|
||||
// no-op
|
||||
} else if (e.data.cmd === 'checkMailbox') {
|
||||
if (initializedJS) {
|
||||
Module['checkMailbox']();
|
||||
}
|
||||
} else if (e.data.cmd) {
|
||||
// The received message looks like something that should be handled by this message
|
||||
// handler, (since there is a e.data.cmd field present), but is not one of the
|
||||
// recognized commands:
|
||||
err('worker.js received unknown command ' + e.data.cmd);
|
||||
err(e.data);
|
||||
}
|
||||
} catch(ex) {
|
||||
err('worker.js onmessage() captured an uncaught exception: ' + ex);
|
||||
if (ex && ex.stack) err(ex.stack);
|
||||
if (Module['__emscripten_thread_crashed']) {
|
||||
Module['__emscripten_thread_crashed']();
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
};
|
||||
|
||||
self.onmessage = handleMessage;
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
|
||||
from php:8.2-apache
|
||||
|
||||
RUN apt update && \
|
||||
apt install -y postgresql-server-dev-15 && \
|
||||
docker-php-ext-install pdo_pgsql && \
|
||||
a2enmod rewrite headers
|
||||
|
||||
RUN rm -rf /var/www/html && \
|
||||
ln -sf /app/public /var/www/html && \
|
||||
sed -i 's/80/8000/' /etc/apache2/ports.conf && \
|
||||
sed -i 's/80/8000/' /etc/apache2/sites-enabled/000-default.conf
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace App\Command;
|
||||
|
||||
use App\Security\AccessTokenHandler;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(name: 'app:login-url', description: 'Add a short description for your command')]
|
||||
class LoginUrlCommand extends Command
|
||||
{
|
||||
|
||||
public function __construct(private AccessTokenHandler $handler)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('auth_name', InputArgument::REQUIRED, 'Authentication name to use.');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$auth_name = $input->getArgument('auth_name');
|
||||
|
||||
$msg = $this->handler->encodeMessage($auth_name, "AUTH_LOGIN_SEED");
|
||||
|
||||
$io->writeln('URL: <href=http://localhost:8000/login/' . $msg . '>/login</>');
|
||||
|
||||
$io->success("");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Security\AccessTokenHandler;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class ClientLoaderController extends AbstractController
|
||||
{
|
||||
|
||||
public function __construct(private AccessTokenHandler $handler)
|
||||
{}
|
||||
|
||||
#[Route('/login/{slug}', name: 'app_client_loader')]
|
||||
public function index(string $slug): Response
|
||||
{
|
||||
$authName = $this->handler->parseMessage($slug, "AUTH_LOGIN_SEED", "5 min");
|
||||
|
||||
$size_pck = filesize("godot/index.pck");
|
||||
$size_wasm = filesize("godot/index.wasm");
|
||||
|
||||
return $this->render('client_loader/index.html.twig',
|
||||
[
|
||||
'controller_name' => $authName,
|
||||
'token' => $this->handler->encodeMessage($authName, 'AUTH_SEED'),
|
||||
'size_wasm' => $size_wasm,
|
||||
'size_pck' => $size_pck
|
||||
]);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
if ! podman inspect ag-server:latest >/dev/null 2>&1; then
|
||||
podman build -f server.Dockerfile -t ag-server:latest
|
||||
fi
|
||||
|
||||
podman run --net=host -v $(pwd):/app -it --rm ag-server:latest
|
@ -0,0 +1,248 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<title>Animegame-Client</title>
|
||||
<style>
|
||||
body {
|
||||
touch-action: none;
|
||||
margin: 0;
|
||||
border: 0 none;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
display: block;
|
||||
margin: 0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#canvas:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.godot {
|
||||
font-family: 'Noto Sans', 'Droid Sans', Arial, sans-serif;
|
||||
color: #e0e0e0;
|
||||
background-color: #3b3943;
|
||||
background-image: linear-gradient(to bottom, #403e48, #35333c);
|
||||
border: 1px solid #45434e;
|
||||
box-shadow: 0 0 1px 1px #2f2d35;
|
||||
}
|
||||
|
||||
/* Status display */
|
||||
|
||||
#status {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
/* don't consume click events - make children visible explicitly */
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#status-progress {
|
||||
width: 366px;
|
||||
height: 7px;
|
||||
background-color: #38363A;
|
||||
border: 1px solid #444246;
|
||||
padding: 1px;
|
||||
box-shadow: 0 0 2px 1px #1B1C22;
|
||||
border-radius: 2px;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@media only screen and (orientation:portrait) {
|
||||
#status-progress {
|
||||
width: 61.8%;
|
||||
}
|
||||
}
|
||||
|
||||
#status-progress-inner {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
box-sizing: border-box;
|
||||
transition: width 0.5s linear;
|
||||
background-color: #202020;
|
||||
border: 1px solid #222223;
|
||||
box-shadow: 0 0 1px 1px #27282E;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#status-indeterminate {
|
||||
height: 42px;
|
||||
visibility: visible;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#status-indeterminate > div {
|
||||
width: 4.5px;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 9px 3px 0 3px;
|
||||
border-color: #2b2b2b transparent transparent transparent;
|
||||
transform-origin: center 21px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#status-indeterminate > div:nth-child(1) { transform: rotate( 22.5deg); }
|
||||
#status-indeterminate > div:nth-child(2) { transform: rotate( 67.5deg); }
|
||||
#status-indeterminate > div:nth-child(3) { transform: rotate(112.5deg); }
|
||||
#status-indeterminate > div:nth-child(4) { transform: rotate(157.5deg); }
|
||||
#status-indeterminate > div:nth-child(5) { transform: rotate(202.5deg); }
|
||||
#status-indeterminate > div:nth-child(6) { transform: rotate(247.5deg); }
|
||||
#status-indeterminate > div:nth-child(7) { transform: rotate(292.5deg); }
|
||||
#status-indeterminate > div:nth-child(8) { transform: rotate(337.5deg); }
|
||||
|
||||
#status-notice {
|
||||
margin: 0 100px;
|
||||
line-height: 1.3;
|
||||
visibility: visible;
|
||||
padding: 4px 6px;
|
||||
visibility: visible;
|
||||
}
|
||||
</style>
|
||||
<link id='-gd-engine-icon' rel='icon' type='image/png' href='/index.icon.png' />
|
||||
<link rel='apple-touch-icon' href='./index.apple-touch-icon.png'/>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas">
|
||||
HTML5 canvas appears to be unsupported in the current browser.<br >
|
||||
Please try updating or use a different browser.
|
||||
</canvas>
|
||||
<div id="status">
|
||||
<div id="status-progress" style="display: none;" oncontextmenu="event.preventDefault();">
|
||||
<div id ="status-progress-inner"></div>
|
||||
</div>
|
||||
<div id="status-indeterminate" style="display: none;" oncontextmenu="event.preventDefault();">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div id="status-notice" class="godot" style="display: none;"></div>
|
||||
</div>
|
||||
|
||||
<script src="/index.js"></script>
|
||||
<script>
|
||||
const GODOT_CONFIG = {"args":[],"canvasResizePolicy":2,"executable":"index","experimentalVK":false,"fileSizes":{"/index.pck":{{ size_pck }},"/index.wasm":{{ size_wasm }}},"focusCanvas":true,"gdextensionLibs":[]};
|
||||
const engine = new Engine(GODOT_CONFIG);
|
||||
|
||||
(function () {
|
||||
const INDETERMINATE_STATUS_STEP_MS = 100;
|
||||
const statusProgress = document.getElementById('status-progress');
|
||||
const statusProgressInner = document.getElementById('status-progress-inner');
|
||||
const statusIndeterminate = document.getElementById('status-indeterminate');
|
||||
const statusNotice = document.getElementById('status-notice');
|
||||
|
||||
let initializing = true;
|
||||
let statusMode = 'hidden';
|
||||
|
||||
let animationCallbacks = [];
|
||||
function animate(time) {
|
||||
animationCallbacks.forEach((callback) => callback(time));
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
function animateStatusIndeterminate(ms) {
|
||||
const i = Math.floor((ms / INDETERMINATE_STATUS_STEP_MS) % 8);
|
||||
if (statusIndeterminate.children[i].style.borderTopColor === '') {
|
||||
Array.prototype.slice.call(statusIndeterminate.children).forEach((child) => {
|
||||
child.style.borderTopColor = '';
|
||||
});
|
||||
statusIndeterminate.children[i].style.borderTopColor = '#dfdfdf';
|
||||
}
|
||||
}
|
||||
|
||||
function setStatusMode(mode) {
|
||||
if (statusMode === mode || !initializing) {
|
||||
return;
|
||||
}
|
||||
[statusProgress, statusIndeterminate, statusNotice].forEach((elem) => {
|
||||
elem.style.display = 'none';
|
||||
});
|
||||
animationCallbacks = animationCallbacks.filter(function (value) {
|
||||
return (value !== animateStatusIndeterminate);
|
||||
});
|
||||
switch (mode) {
|
||||
case 'progress':
|
||||
statusProgress.style.display = 'block';
|
||||
break;
|
||||
case 'indeterminate':
|
||||
statusIndeterminate.style.display = 'block';
|
||||
animationCallbacks.push(animateStatusIndeterminate);
|
||||
break;
|
||||
case 'notice':
|
||||
statusNotice.style.display = 'block';
|
||||
break;
|
||||
case 'hidden':
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid status mode');
|
||||
}
|
||||
statusMode = mode;
|
||||
}
|
||||
|
||||
function setStatusNotice(text) {
|
||||
while (statusNotice.lastChild) {
|
||||
statusNotice.removeChild(statusNotice.lastChild);
|
||||
}
|
||||
const lines = text.split('\n');
|
||||
lines.forEach((line) => {
|
||||
statusNotice.appendChild(document.createTextNode(line));
|
||||
statusNotice.appendChild(document.createElement('br'));
|
||||
});
|
||||
}
|
||||
|
||||
function displayFailureNotice(err) {
|
||||
const msg = err.message || err;
|
||||
console.error(msg);
|
||||
setStatusNotice(msg);
|
||||
setStatusMode('notice');
|
||||
initializing = false;
|
||||
}
|
||||
|
||||
const missing = Engine.getMissingFeatures();
|
||||
if (missing.length !== 0) {
|
||||
const missingMsg = 'Error\nThe following features required to run Godot projects on the Web are missing:\n';
|
||||
displayFailureNotice(missingMsg + missing.join('\n'));
|
||||
} else {
|
||||
setStatusMode('indeterminate');
|
||||
engine.startGame({
|
||||
'onProgress': function (current, total) {
|
||||
if (total > 0) {
|
||||
statusProgressInner.style.width = `${(current / total) * 100}%`;
|
||||
setStatusMode('progress');
|
||||
if (current === total) {
|
||||
// wait for progress bar animation
|
||||
setTimeout(() => {
|
||||
setStatusMode('indeterminate');
|
||||
}, 500);
|
||||
}
|
||||
} else {
|
||||
setStatusMode('indeterminate');
|
||||
}
|
||||
},
|
||||
}).then(() => {
|
||||
setStatusMode('hidden');
|
||||
initializing = false;
|
||||
}, displayFailureNotice);
|
||||
}
|
||||
}());
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -0,0 +1,22 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Hello ClientLoaderController!{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<style>
|
||||
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
|
||||
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
|
||||
</style>
|
||||
|
||||
<div class="example-wrapper">
|
||||
<h1>Hello {{ controller_name }}! ✅</h1>
|
||||
|
||||
<p style="white-space:nowrap">Token: {{ token }}</p>
|
||||
|
||||
This friendly message is coming from:
|
||||
<ul>
|
||||
<li>Your controller at <code><a href="{{ '/home/pascal/git/animegame-dojo/src/Controller/ClientLoaderController.php'|file_link(0) }}">src/Controller/ClientLoaderController.php</a></code></li>
|
||||
<li>Your template at <code><a href="{{ '/home/pascal/git/animegame-dojo/templates/client_loader/index.html.twig'|file_link(0) }}">templates/client_loader/index.html.twig</a></code></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,248 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<title>Animegame-Client</title>
|
||||
<style>
|
||||
body {
|
||||
touch-action: none;
|
||||
margin: 0;
|
||||
border: 0 none;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
display: block;
|
||||
margin: 0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#canvas:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.godot {
|
||||
font-family: 'Noto Sans', 'Droid Sans', Arial, sans-serif;
|
||||
color: #e0e0e0;
|
||||
background-color: #3b3943;
|
||||
background-image: linear-gradient(to bottom, #403e48, #35333c);
|
||||
border: 1px solid #45434e;
|
||||
box-shadow: 0 0 1px 1px #2f2d35;
|
||||
}
|
||||
|
||||
/* Status display */
|
||||
|
||||
#status {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
/* don't consume click events - make children visible explicitly */
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#status-progress {
|
||||
width: 366px;
|
||||
height: 7px;
|
||||
background-color: #38363A;
|
||||
border: 1px solid #444246;
|
||||
padding: 1px;
|
||||
box-shadow: 0 0 2px 1px #1B1C22;
|
||||
border-radius: 2px;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@media only screen and (orientation:portrait) {
|
||||
#status-progress {
|
||||
width: 61.8%;
|
||||
}
|
||||
}
|
||||
|
||||
#status-progress-inner {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
box-sizing: border-box;
|
||||
transition: width 0.5s linear;
|
||||
background-color: #202020;
|
||||
border: 1px solid #222223;
|
||||
box-shadow: 0 0 1px 1px #27282E;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#status-indeterminate {
|
||||
height: 42px;
|
||||
visibility: visible;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#status-indeterminate > div {
|
||||
width: 4.5px;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 9px 3px 0 3px;
|
||||
border-color: #2b2b2b transparent transparent transparent;
|
||||
transform-origin: center 21px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#status-indeterminate > div:nth-child(1) { transform: rotate( 22.5deg); }
|
||||
#status-indeterminate > div:nth-child(2) { transform: rotate( 67.5deg); }
|
||||
#status-indeterminate > div:nth-child(3) { transform: rotate(112.5deg); }
|
||||
#status-indeterminate > div:nth-child(4) { transform: rotate(157.5deg); }
|
||||
#status-indeterminate > div:nth-child(5) { transform: rotate(202.5deg); }
|
||||
#status-indeterminate > div:nth-child(6) { transform: rotate(247.5deg); }
|
||||
#status-indeterminate > div:nth-child(7) { transform: rotate(292.5deg); }
|
||||
#status-indeterminate > div:nth-child(8) { transform: rotate(337.5deg); }
|
||||
|
||||
#status-notice {
|
||||
margin: 0 100px;
|
||||
line-height: 1.3;
|
||||
visibility: visible;
|
||||
padding: 4px 6px;
|
||||
visibility: visible;
|
||||
}
|
||||
</style>
|
||||
<link id='-gd-engine-icon' rel='icon' type='image/png' href='{{ asset('godot/index.icon.png') }}' />
|
||||
<link rel='apple-touch-icon' href='{{ asset('godot/index.apple-touch-icon.png') }}'/>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas">
|
||||
HTML5 canvas appears to be unsupported in the current browser.<br >
|
||||
Please try updating or use a different browser.
|
||||
</canvas>
|
||||
<div id="status">
|
||||
<div id="status-progress" style="display: none;" oncontextmenu="event.preventDefault();">
|
||||
<div id ="status-progress-inner"></div>
|
||||
</div>
|
||||
<div id="status-indeterminate" style="display: none;" oncontextmenu="event.preventDefault();">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div id="status-notice" class="godot" style="display: none;"></div>
|
||||
</div>
|
||||
|
||||
<script src="{{ asset('godot/index.js') }}"></script>
|
||||
<script>
|
||||
const GODOT_CONFIG = {"args":["--", "--token={{ token }}"],"canvasResizePolicy":2,"executable":"{{ asset('godot/index') }}","experimentalVK":false,"fileSizes":{"{{ asset('godot/index.pck') }}":355472,"{{ asset('godot/index.wasm') }}":35708238},"focusCanvas":true,"gdextensionLibs":[]};
|
||||
const engine = new Engine(GODOT_CONFIG);
|
||||
|
||||
(function () {
|
||||
const INDETERMINATE_STATUS_STEP_MS = 100;
|
||||
const statusProgress = document.getElementById('status-progress');
|
||||
const statusProgressInner = document.getElementById('status-progress-inner');
|
||||
const statusIndeterminate = document.getElementById('status-indeterminate');
|
||||
const statusNotice = document.getElementById('status-notice');
|
||||
|
||||
let initializing = true;
|
||||
let statusMode = 'hidden';
|
||||
|
||||
let animationCallbacks = [];
|
||||
function animate(time) {
|
||||
animationCallbacks.forEach((callback) => callback(time));
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
function animateStatusIndeterminate(ms) {
|
||||
const i = Math.floor((ms / INDETERMINATE_STATUS_STEP_MS) % 8);
|
||||
if (statusIndeterminate.children[i].style.borderTopColor === '') {
|
||||
Array.prototype.slice.call(statusIndeterminate.children).forEach((child) => {
|
||||
child.style.borderTopColor = '';
|
||||
});
|
||||
statusIndeterminate.children[i].style.borderTopColor = '#dfdfdf';
|
||||
}
|
||||
}
|
||||
|
||||
function setStatusMode(mode) {
|
||||
if (statusMode === mode || !initializing) {
|
||||
return;
|
||||
}
|
||||
[statusProgress, statusIndeterminate, statusNotice].forEach((elem) => {
|
||||
elem.style.display = 'none';
|
||||
});
|
||||
animationCallbacks = animationCallbacks.filter(function (value) {
|
||||
return (value !== animateStatusIndeterminate);
|
||||
});
|
||||
switch (mode) {
|
||||
case 'progress':
|
||||
statusProgress.style.display = 'block';
|
||||
break;
|
||||
case 'indeterminate':
|
||||
statusIndeterminate.style.display = 'block';
|
||||
animationCallbacks.push(animateStatusIndeterminate);
|
||||
break;
|
||||
case 'notice':
|
||||
statusNotice.style.display = 'block';
|
||||
break;
|
||||
case 'hidden':
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid status mode');
|
||||
}
|
||||
statusMode = mode;
|
||||
}
|
||||
|
||||
function setStatusNotice(text) {
|
||||
while (statusNotice.lastChild) {
|
||||
statusNotice.removeChild(statusNotice.lastChild);
|
||||
}
|
||||
const lines = text.split('\n');
|
||||
lines.forEach((line) => {
|
||||
statusNotice.appendChild(document.createTextNode(line));
|
||||
statusNotice.appendChild(document.createElement('br'));
|
||||
});
|
||||
}
|
||||
|
||||
function displayFailureNotice(err) {
|
||||
const msg = err.message || err;
|
||||
console.error(msg);
|
||||
setStatusNotice(msg);
|
||||
setStatusMode('notice');
|
||||
initializing = false;
|
||||
}
|
||||
|
||||
const missing = Engine.getMissingFeatures();
|
||||
if (missing.length !== 0) {
|
||||
const missingMsg = 'Error\nThe following features required to run Godot projects on the Web are missing:\n';
|
||||
displayFailureNotice(missingMsg + missing.join('\n'));
|
||||
} else {
|
||||
setStatusMode('indeterminate');
|
||||
engine.startGame({
|
||||
'onProgress': function (current, total) {
|
||||
if (total > 0) {
|
||||
statusProgressInner.style.width = `${(current / total) * 100}%`;
|
||||
setStatusMode('progress');
|
||||
if (current === total) {
|
||||
// wait for progress bar animation
|
||||
setTimeout(() => {
|
||||
setStatusMode('indeterminate');
|
||||
}, 500);
|
||||
}
|
||||
} else {
|
||||
setStatusMode('indeterminate');
|
||||
}
|
||||
},
|
||||
}).then(() => {
|
||||
setStatusMode('hidden');
|
||||
initializing = false;
|
||||
}, displayFailureNotice);
|
||||
}
|
||||
}());
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace App\Tests;
|
||||
|
||||
use App\Factory\UserFactory;
|
||||
|
||||
class UserTest extends AbstractTest
|
||||
{
|
||||
|
||||
/**
|
||||
* Requirement: A user should be able to update his properties
|
||||
*/
|
||||
public function testUpdateUserProperties(): void
|
||||
{
|
||||
$requestUser = UserFactory::createOne();
|
||||
|
||||
$response = static::createClientWithToken($requestUser->authName)->request('PATCH', $this->getIri($requestUser),
|
||||
[
|
||||
'headers' => [
|
||||
'content-type' => 'application/merge-patch+json'
|
||||
],
|
||||
'json' => [
|
||||
'authName' => 'foo.bar',
|
||||
'properties' => [
|
||||
'a' => 1,
|
||||
'b' => true,
|
||||
'c' => null,
|
||||
'd' => 'text'
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertResponseStatusCodeSame(200);
|
||||
|
||||
$this->assertEquals(1, $response->toArray()['properties']['a']);
|
||||
$this->assertEquals(true, $response->toArray()['properties']['b']);
|
||||
$this->assertEquals(null, $response->toArray()['properties']['c']);
|
||||
$this->assertEquals('text', $response->toArray()['properties']['d']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requirement: A user should NOT be able to update his authName
|
||||
*/
|
||||
public function testUpdateUserAuthnameNotPossible(): void
|
||||
{
|
||||
$requestUser = UserFactory::createOne();
|
||||
|
||||
$response = static::createClientWithToken($requestUser->authName)->request('PATCH', $this->getIri($requestUser),
|
||||
[
|
||||
'headers' => [
|
||||
'content-type' => 'application/merge-patch+json'
|
||||
],
|
||||
'json' => [
|
||||
'authName' => 'foo.bar'
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertResponseStatusCodeSame(200);
|
||||
|
||||
$this->assertNotEquals("foo.bar", $response->toArray()['authName']);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in new issue