Adding pj selection.

This commit is contained in:
Sergiotarxz 2023-06-08 09:02:32 +02:00
parent caa8fa7b75
commit aa9c652fcb
30 changed files with 2402 additions and 102 deletions

View File

@ -19,6 +19,7 @@ my $build = Module::Build->new(
'DBIx::Class::DeploymentHandler' => 0,
'UUID::URandom' => 0,
'Module::Pluggable' => 0,
'Redis' => 0,
},
);
$build->create_build_script;

View File

@ -0,0 +1,18 @@
--
-- Created by SQL::Translator::Producer::PostgreSQL
-- Created on Mon Jun 5 04:01:49 2023
--
;
--
-- Table: dbix_class_deploymenthandler_versions
--
CREATE TABLE "dbix_class_deploymenthandler_versions" (
"id" serial NOT NULL,
"version" character varying(50) NOT NULL,
"ddl" text,
"upgrade_sql" text,
PRIMARY KEY ("id"),
CONSTRAINT "dbix_class_deploymenthandler_versions_version" UNIQUE ("version")
);
;

View File

@ -0,0 +1,258 @@
--
-- Created by SQL::Translator::Producer::PostgreSQL
-- Created on Mon Jun 5 04:02:06 2023
--
;
--
-- Table: equipment
--
CREATE TABLE "equipment" (
"uuid" uuid NOT NULL,
PRIMARY KEY ("uuid")
);
;
--
-- Table: inventories
--
CREATE TABLE "inventories" (
"uuid" uuid NOT NULL,
PRIMARY KEY ("uuid")
);
;
--
-- Table: players
--
CREATE TABLE "players" (
"uuid" uuid NOT NULL,
"username" text NOT NULL,
"encrypted_password" text NOT NULL,
"email" text NOT NULL,
"verified" boolean NOT NULL,
"verification_token" text,
"register_date" timestamp DEFAULT NOW() NOT NULL,
"last_activity" timestamp DEFAULT NOW() NOT NULL,
PRIMARY KEY ("uuid"),
CONSTRAINT "unique_constraint_email" UNIQUE ("email"),
CONSTRAINT "unique_constraint_username" UNIQUE ("username")
);
;
--
-- Table: skill_like_lists
--
CREATE TABLE "skill_like_lists" (
"uuid" uuid DEFAULT uuid_generate_v4() NOT NULL,
PRIMARY KEY ("uuid")
);
;
--
-- Table: stats
--
CREATE TABLE "stats" (
"uuid" uuid NOT NULL,
"health" integer NOT NULL,
"mana" integer NOT NULL,
"strength" integer NOT NULL,
"resistance" integer NOT NULL,
"magic" integer NOT NULL,
"speed" integer NOT NULL,
"intelligence" integer NOT NULL,
PRIMARY KEY ("uuid")
);
;
--
-- Table: equipment_items
--
CREATE TABLE "equipment_items" (
"kind" text NOT NULL,
"equipment" uuid NOT NULL,
"identifier" text NOT NULL,
"quantity" integer NOT NULL,
PRIMARY KEY ("kind", "equipment")
);
CREATE INDEX "equipment_items_idx_equipment" on "equipment_items" ("equipment");
;
--
-- Table: inventory_items
--
CREATE TABLE "inventory_items" (
"uuid" uuid DEFAULT uuid_generate_v4() NOT NULL,
"inventory" uuid NOT NULL,
"identifier" text NOT NULL,
"quantity" integer NOT NULL,
PRIMARY KEY ("uuid")
);
CREATE INDEX "inventory_items_idx_inventory" on "inventory_items" ("inventory");
;
--
-- Table: skill_like_items
--
CREATE TABLE "skill_like_items" (
"identifier" text NOT NULL,
"owner_list" uuid NOT NULL,
"level" integer DEFAULT 1 NOT NULL,
PRIMARY KEY ("identifier", "owner_list")
);
CREATE INDEX "skill_like_items_idx_owner_list" on "skill_like_items" ("owner_list");
;
--
-- Table: teams
--
CREATE TABLE "teams" (
"uuid" uuid NOT NULL,
"leader" uuid,
"name" text NOT NULL,
"planet" text NOT NULL,
"super_area" text NOT NULL,
"area" text NOT NULL,
"location" text NOT NULL,
PRIMARY KEY ("uuid"),
CONSTRAINT "u_name" UNIQUE ("name")
);
CREATE INDEX "teams_idx_leader" on "teams" ("leader");
;
--
-- Table: player_pjs
--
CREATE TABLE "player_pjs" (
"uuid" uuid DEFAULT uuid_generate_v4() NOT NULL,
"owner" uuid NOT NULL,
"full_name" text NOT NULL,
"short_name" text NOT NULL,
"nick" text NOT NULL,
"race" text NOT NULL,
"team" uuid NOT NULL,
"creation_date" timestamp DEFAULT NOW() NOT NULL,
"last_activity" timestamp DEFAULT NOW() NOT NULL,
"experience" integer DEFAULT 1 NOT NULL,
"equipment" uuid NOT NULL,
"born_stats" uuid NOT NULL,
"training_stats" uuid NOT NULL,
"skills" uuid NOT NULL,
"spells" uuid NOT NULL,
"inventory" uuid NOT NULL,
"health" integer NOT NULL,
"mana" integer NOT NULL,
PRIMARY KEY ("uuid")
);
CREATE INDEX "player_pjs_idx_born_stats" on "player_pjs" ("born_stats");
CREATE INDEX "player_pjs_idx_equipment" on "player_pjs" ("equipment");
CREATE INDEX "player_pjs_idx_inventory" on "player_pjs" ("inventory");
CREATE INDEX "player_pjs_idx_owner" on "player_pjs" ("owner");
CREATE INDEX "player_pjs_idx_skills" on "player_pjs" ("skills");
CREATE INDEX "player_pjs_idx_spells" on "player_pjs" ("spells");
CREATE INDEX "player_pjs_idx_team" on "player_pjs" ("team");
CREATE INDEX "player_pjs_idx_training_stats" on "player_pjs" ("training_stats");
;
--
-- Table: player_companion_npcs
--
CREATE TABLE "player_companion_npcs" (
"uuid" uuid DEFAULT uuid_generate_v4() NOT NULL,
"owner" uuid NOT NULL,
"identifier" text NOT NULL,
"nick" text,
"race" text NOT NULL,
"level" integer DEFAULT 1 NOT NULL,
"exp" integer DEFAULT 1 NOT NULL,
"equipment" uuid NOT NULL,
"stats" uuid NOT NULL,
"skills" uuid NOT NULL,
"spells" uuid NOT NULL,
"inventory" uuid NOT NULL,
PRIMARY KEY ("uuid")
);
CREATE INDEX "player_companion_npcs_idx_equipment" on "player_companion_npcs" ("equipment");
CREATE INDEX "player_companion_npcs_idx_inventory" on "player_companion_npcs" ("inventory");
CREATE INDEX "player_companion_npcs_idx_owner" on "player_companion_npcs" ("owner");
CREATE INDEX "player_companion_npcs_idx_skills" on "player_companion_npcs" ("skills");
CREATE INDEX "player_companion_npcs_idx_spells" on "player_companion_npcs" ("spells");
CREATE INDEX "player_companion_npcs_idx_stats" on "player_companion_npcs" ("stats");
;
--
-- Foreign Key Definitions
--
;
ALTER TABLE "equipment_items" ADD CONSTRAINT "equipment_items_fk_equipment" FOREIGN KEY ("equipment")
REFERENCES "equipment" ("uuid") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
;
ALTER TABLE "inventory_items" ADD CONSTRAINT "inventory_items_fk_inventory" FOREIGN KEY ("inventory")
REFERENCES "inventories" ("uuid") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
;
ALTER TABLE "skill_like_items" ADD CONSTRAINT "skill_like_items_fk_owner_list" FOREIGN KEY ("owner_list")
REFERENCES "skill_like_lists" ("uuid") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
;
ALTER TABLE "teams" ADD CONSTRAINT "teams_fk_leader" FOREIGN KEY ("leader")
REFERENCES "player_pjs" ("uuid") DEFERRABLE;
;
ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_born_stats" FOREIGN KEY ("born_stats")
REFERENCES "stats" ("uuid") DEFERRABLE;
;
ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_equipment" FOREIGN KEY ("equipment")
REFERENCES "equipment" ("uuid") DEFERRABLE;
;
ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_inventory" FOREIGN KEY ("inventory")
REFERENCES "inventories" ("uuid") DEFERRABLE;
;
ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_owner" FOREIGN KEY ("owner")
REFERENCES "players" ("uuid") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
;
ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_skills" FOREIGN KEY ("skills")
REFERENCES "skill_like_lists" ("uuid") DEFERRABLE;
;
ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_spells" FOREIGN KEY ("spells")
REFERENCES "skill_like_lists" ("uuid") DEFERRABLE;
;
ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_team" FOREIGN KEY ("team")
REFERENCES "teams" ("uuid") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
;
ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_training_stats" FOREIGN KEY ("training_stats")
REFERENCES "stats" ("uuid") DEFERRABLE;
;
ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_equipment" FOREIGN KEY ("equipment")
REFERENCES "equipment" ("uuid") DEFERRABLE;
;
ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_inventory" FOREIGN KEY ("inventory")
REFERENCES "inventories" ("uuid") DEFERRABLE;
;
ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_owner" FOREIGN KEY ("owner")
REFERENCES "player_pjs" ("uuid") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
;
ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_skills" FOREIGN KEY ("skills")
REFERENCES "skill_like_lists" ("uuid") DEFERRABLE;
;
ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_spells" FOREIGN KEY ("spells")
REFERENCES "skill_like_lists" ("uuid") DEFERRABLE;
;
ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_stats" FOREIGN KEY ("stats")
REFERENCES "stats" ("uuid") DEFERRABLE;
;

View File

@ -0,0 +1,66 @@
-- Convert schema '/home/sergio/LasTres/script/../dbicdh/_source/deploy/1/001-auto.yml' to '/home/sergio/LasTres/script/../dbicdh/_source/deploy/4/001-auto.yml':;
;
BEGIN;
;
ALTER TABLE player_pjs DROP CONSTRAINT player_pjs_fk_owner;
;
ALTER TABLE player_pjs DROP CONSTRAINT player_pjs_fk_stats;
;
DROP INDEX player_pjs_idx_stats;
;
ALTER TABLE player_pjs DROP COLUMN level;
;
ALTER TABLE player_pjs DROP COLUMN exp;
;
ALTER TABLE player_pjs DROP COLUMN stats;
;
ALTER TABLE player_pjs ADD COLUMN experience integer DEFAULT 1 NOT NULL;
;
ALTER TABLE player_pjs ADD COLUMN born_stats uuid NOT NULL;
;
ALTER TABLE player_pjs ADD COLUMN training_stats uuid NOT NULL;
;
ALTER TABLE player_pjs ADD COLUMN health integer NOT NULL;
;
ALTER TABLE player_pjs ADD COLUMN mana integer NOT NULL;
;
CREATE INDEX player_pjs_idx_born_stats on player_pjs (born_stats);
;
CREATE INDEX player_pjs_idx_training_stats on player_pjs (training_stats);
;
ALTER TABLE player_pjs ADD CONSTRAINT player_pjs_fk_born_stats FOREIGN KEY (born_stats)
REFERENCES stats (uuid) DEFERRABLE;
;
ALTER TABLE player_pjs ADD CONSTRAINT player_pjs_fk_owner FOREIGN KEY (owner)
REFERENCES players (uuid) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
;
ALTER TABLE player_pjs ADD CONSTRAINT player_pjs_fk_training_stats FOREIGN KEY (training_stats)
REFERENCES stats (uuid) DEFERRABLE;
;
ALTER TABLE stats DROP COLUMN charisma;
;
ALTER TABLE teams ALTER COLUMN leader DROP NOT NULL;
;
COMMIT;

View File

@ -0,0 +1,15 @@
-- Convert schema '/home/sergio/LasTres/script/../dbicdh/_source/deploy/3/001-auto.yml' to '/home/sergio/LasTres/script/../dbicdh/_source/deploy/4/001-auto.yml':;
;
BEGIN;
;
ALTER TABLE player_pjs ADD COLUMN health integer NOT NULL;
;
ALTER TABLE player_pjs ADD COLUMN mana integer NOT NULL;
;
COMMIT;

View File

@ -0,0 +1,91 @@
---
schema:
procedures: {}
tables:
dbix_class_deploymenthandler_versions:
constraints:
- deferrable: 1
expression: ''
fields:
- id
match_type: ''
name: ''
on_delete: ''
on_update: ''
options: []
reference_fields: []
reference_table: ''
type: PRIMARY KEY
- deferrable: 1
expression: ''
fields:
- version
match_type: ''
name: dbix_class_deploymenthandler_versions_version
on_delete: ''
on_update: ''
options: []
reference_fields: []
reference_table: ''
type: UNIQUE
fields:
ddl:
data_type: text
default_value: ~
is_nullable: 1
is_primary_key: 0
is_unique: 0
name: ddl
order: 3
size:
- 0
id:
data_type: int
default_value: ~
is_auto_increment: 1
is_nullable: 0
is_primary_key: 1
is_unique: 0
name: id
order: 1
size:
- 0
upgrade_sql:
data_type: text
default_value: ~
is_nullable: 1
is_primary_key: 0
is_unique: 0
name: upgrade_sql
order: 4
size:
- 0
version:
data_type: varchar
default_value: ~
is_nullable: 0
is_primary_key: 0
is_unique: 1
name: version
order: 2
size:
- 50
indices: []
name: dbix_class_deploymenthandler_versions
options: []
order: 1
triggers: {}
views: {}
translator:
add_drop_table: 0
filename: ~
no_comments: 0
parser_args:
sources:
- __VERSION
parser_type: SQL::Translator::Parser::DBIx::Class
producer_args: {}
producer_type: SQL::Translator::Producer::YAML
show_warnings: 0
trace: 0
version: 1.63

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,14 @@
import * as React from 'react'
import { PJ } from '@lastres/pj'
import UpperPanel from '@lastres/components/upper-panel'
import BottomPanel from '@lastres/components/bottom-panel'
import PJSelectionMenu from '@lastres/components/pj-selection-menu'
export interface GameProps {
setSelectedPJ: (set: string | null) => void
selectedPJ: string | null
setSelectedPJ: (set: PJ | null) => void
selectedPJ: PJ | null
userWantsToCreatePJ: boolean
setUserWantsToCreatePJ: (set: boolean) => void
error: string | null
@ -26,6 +28,18 @@ export default function Game (props: GameProps): JSX.Element {
</>
)
}
window.setTimeout(() => {
const locationProtocol = window.location.protocol
if (locationProtocol == null) {
return
}
const protocol = locationProtocol.match(/https:/) != null ? 'wss' : 'ws'
const webSocket = new WebSocket(`${protocol}://${window.location.host}/ws`)
webSocket.onopen = () => {
webSocket.send(JSON.stringify({hola: "mundo"}))
};
}, 1);
return (
<>
<UpperPanel/>

View File

@ -1,5 +1,7 @@
import * as React from 'react'
import { PJ } from '@lastres/pj'
import LoginPage from '@lastres/components/login-page'
import Game from '@lastres/components/game'
import { checkLogin } from '@lastres/login'
@ -11,7 +13,7 @@ export default function Page (): JSX.Element {
const [userWantsToCreatePJ, setUserWantsToCreatePJ] = React.useState<boolean>(false)
const [isAskingForRegistration, setIsAskingForRegistration] = React.useState<boolean>(false)
const [error, setError] = React.useState<string | null>(null)
const [selectedPJ, setSelectedPJ] = React.useState<string | null>(null)
const [selectedPJ, setSelectedPJ] = React.useState<PJ | null>(null)
checkLogin(setError, setIsLoggedIn)
if (!isLoggedIn) {
return notLoggedRender(setIsLoggedIn, isAskingForRegistration, setIsAskingForRegistration, error, setError)

View File

@ -1,8 +1,10 @@
import * as React from 'react'
import { PJ } from '@lastres/pj'
export interface PJCreationMenuProps {
error: string | null
setSelectedPJ: (set: string | null) => void
setSelectedPJ: (set: PJ | null) => void
setUserWantsToCreatePJ: (set: boolean) => void
setError: (set: string | null) => void
}

View File

@ -0,0 +1,14 @@
import * as React from 'react'
export interface PJHealthLikeBarProps {
value: number
max: number
}
export default function PJHealthLikeBar(props: PJHealthLikeBarProps): JSX.Element {
const percentage = ((props.value / props.max) * 100) + '%';
return (
<div className="bar">
<div className="filled" style={{width: percentage}}></div>
</div>
)
}

View File

@ -0,0 +1,42 @@
import * as React from 'react'
import { PJ } from '@lastres/pj'
import PJHealthLikeBar from '@lastres/components/pj-health-like-bar'
interface PJListSelectionProps {
pjs: PJ[] | null
setSelectedPJ: (set: PJ | null) => void
}
export default function PJListSelection(props: PJListSelectionProps) {
const pjs = props.pjs
if (pjs === null) {
return (
<>
</>
)
}
return (
<>
{
pjs.map( (item, i) =>
<a onClick={() => {
props.setSelectedPJ(item)
}}
href="#"
key={i}>
<span>{item.full_name}</span>
<span>{item.short_name}</span>
<span>{item.nick}</span>
<label>
Salud
<PJHealthLikeBar value={item.health} max={item.max_health}/>
</label>
<label>
Mana
<PJHealthLikeBar value={item.mana} max={item.max_mana}/>
</label>
</a> )
}
</>
)
}

View File

@ -1,8 +1,10 @@
import * as React from 'react'
import PJCreationMenu from '@lastres/components/pj-creation-menu'
import PJListSelection from '@lastres/components/pj-list-selection'
import { PJ, fetchMyPjs } from '@lastres/pj'
export interface PJSelectionMenuProps {
setSelectedPJ: (set: string | null) => void
setSelectedPJ: (set: PJ | null) => void
setUserWantsToCreatePJ: (set: boolean) => void
userWantsToCreatePJ: boolean
error: string | null
@ -10,6 +12,7 @@ export interface PJSelectionMenuProps {
}
export default function PJSelectionMenu (props: PJSelectionMenuProps): JSX.Element {
const [pjs, setPJs] = React.useState<PJ[] | null>(null)
const createPJ = (): void => {
props.setUserWantsToCreatePJ(true)
}
@ -22,6 +25,10 @@ export default function PJSelectionMenu (props: PJSelectionMenuProps): JSX.Eleme
setError={props.setError}/>
)
}
if (pjs === null) {
fetchMyPjs(props.setError)
.then((pjs)=> { setPJs(pjs) })
}
return (
<div className="pj-selection-menu">
<div className="pj-selection-menu-container">
@ -29,6 +36,7 @@ export default function PJSelectionMenu (props: PJSelectionMenuProps): JSX.Eleme
<h2>Selecciona un Personaje</h2>
<div className="pj-list">
<a onClick={createPJ} href="#">Crear un nuevo personaje</a>
<PJListSelection setSelectedPJ={props.setSelectedPJ} pjs={pjs}/>
</div>
</div>
</div>

32
js-src/pj.ts Normal file
View File

@ -0,0 +1,32 @@
export interface PJ {
full_name: string
short_name: string
nick: string
health: number
mana: number
max_mana: number
max_health: number
race: string
uuid: string
}
export async function fetchMyPjs(setError: (set: string | null) => void): Promise<PJ[]> {
const response = await fetch('/my/pjs', {
method: 'GET',
mode: 'same-origin',
cache: 'no-cache'
}).catch((error) => {
console.log(error);
setError('Error recuperando tus pjs')
})
if (response === undefined) {
return []
}
const statusCode = response.status
const data = await response.json()
if (statusCode !== 200) {
setError(data.error)
return []
}
return data
}

View File

@ -34,11 +34,12 @@ sub startup ($self) {
my $sessions = Mojolicious::Sessions->new;
$sessions->cookie_name('LasTres');
$sessions->default_expiration(86400);
$sessions->default_expiration(0);
# Normal route to controller
$r->get('/')->to('Root#index');
$r->get('/races/playable')->to('Race#playable');
$r->get('/my/pjs')->to('PJ#list_my_pjs');
$r->post('/player/register')->to('Player#register');
$r->post('/player/login')->to('Player#login');

View File

@ -7,6 +7,8 @@ use warnings;
use UUID::URandom qw/create_uuid_string/;
use Try::Tiny;
use LasTres::DAO::PJs;
use LasTres::DAO::Equipments;
use LasTres::DAO::Teams;
@ -26,6 +28,36 @@ my $result_set_stats = LasTres::DAO::Stats->ResultSet;
my $result_set_skill_like_list = LasTres::DAO::SkillLikeLists->ResultSet;
my $result_set_inventory = LasTres::DAO::Inventories->ResultSet;
sub list_my_pjs ($self) {
my $user = $self->user;
if ( !defined $user ) {
return $self->render(
status => 401,
json => { error => 'You must login first.' }
);
}
my @pjs = $user->pjs;
my @pjs_hash = (
map {
{
uuid => $_->uuid,
full_name => $_->full_name,
short_name => $_->short_name,
nick => $_->nick,
race => $_->race,
health => $_->health,
max_health => $_->max_health,
mana => $_->mana,
max_mana => $_->max_mana,
}
} @pjs
);
return $self->render(
status => 200,
json => \@pjs_hash,
);
}
sub create ($self) {
my %param = %{ $self->req->json };
my $user = $self->user;
@ -77,19 +109,23 @@ sub create ($self) {
if ( !defined $race ) {
return $self->_invalid_race;
}
eval {
my $exception;
try {
$schema->txn_do(
sub {
$self->_insert_new_player( $owner, $full_name, $short_name,
$nick, $race );
}
);
};
if ($@) {
if ( $@ =~ /Rollback failed/ ) {
}
catch {
$exception = $_;
if ( $_ =~ /Rollback failed/ ) {
say STDERR "Unable to rollback failed transaction:";
}
say STDERR $@;
say STDERR $_;
};
if ( defined $exception ) {
return $self->render(
status => 500,
json => { error => 'Database error' },
@ -172,9 +208,10 @@ sub _insert_new_player ( $self, $owner, $full_name, $short_name, $nick, $race )
skills => $uuid_skills,
spells => $uuid_spells,
inventory => $uuid_inventory,
health => 999,
mana => 999,
}
)->insert;
die "Error setting leader";
$team->leader($uuid_pj);
$team->update;
}

View File

@ -5,7 +5,21 @@ use v5.36.0;
use strict;
use warnings;
use JSON qw/encode_json/;
use UUID::URandom qw/create_uuid_string/;
use Mojo::Base 'Mojolicious::Controller', -signatures;
use Data::Dumper;
use LasTres::Redis;
use LasTres::DAO::PJs;
use LasTres::Controller::Websocket::InputPackets;
my %sessions;
my $result_set_pjs = LasTres::DAO::PJs->ResultSet;
my $redis = LasTres::Redis->new;
my $input_packets = LasTres::Controller::Websocket::InputPackets->new;
sub ws ($self) {
my $user = $self->user;
@ -17,16 +31,45 @@ sub ws($self) {
}
);
}
if (!$user->verified) {
return $self->render(
status => 401,
json => {
error => 'Your user is not verified.',
my $session_uuid = create_uuid_string;
$sessions{$session_uuid} = {
user => $user,
controller => $self,
uuid => $session_uuid,
};
my $session = $sessions{$session_uuid};
$self->on(
json => sub ( $self, $hash ) {
$self->_handle_packet( $session, $hash );
}
);
$self->on(
finish => sub ( $self, $code, $reason ) {
delete $sessions{$session_uuid};
say STDERR
"Websocket for user @{[$user->username]} closed with status $code and reason $reason.";
}
);
}
$self->on(json => sub($self, $hash) {
$self->_handle_packet($user, $hash);
});
{
sub _handle_packet ( $self, $session, $hash ) {
my $command = $hash->{command};
if ( !defined $command ) {
say STDERR "No command.";
$self->send( encode_json( { error => "No command" } ) );
return;
}
my $input_packet = $input_packets->hash->{$command)
if ( !defined $input_packet ) {
say STDERR "Unknown command $command.";
$self->send(
encode_json( { error => "Unknown command $command" } ) );
return;
}
my $data = $hash->{data};
return $input_packet->handle( $self, $session, $data );
}
}
1;

View File

@ -0,0 +1,13 @@
package LasTres::Controller::Websocket::InputPacket;
use v5.36.0;
use strict;
use warnings;
use feature 'signatures';
use Moo::Role;
requires qw/new handle identifier/;
1;

View File

@ -0,0 +1,42 @@
package LasTres::Controller::Websocket::InputPacket::Init;
use v5.36.0;
use strict;
use warnings;
use Moo;
use LasTres::Redis;
with 'LasTres::Controller::Websocket::InputPacket';
sub identifier {
return 'init';
}
my $redis = LasTres::Redis->new;
sub handle ( $self, $ws, $session, $data ) {
if (ref $data ne 'HASH') {
return $ws->send( encode_json( { error => "Data should be a hashref." } ) );
}
my $pj_uuid = $data->{pj_uuid};
if ( !defined $pj_uuid ) {
return $ws->send( encode_json( { error => "No pj sent." } ) );
}
my @pjs = $result_set_pjs->search( { uuid => $pj_uuid } );
if ( !scalar @pjs ) {
return $ws->send(
encode_json( { error => 'This pj does not exists' } ) );
}
my $pj = $pjs[0];
my $user = $session->{user};
if ( $pj->owner->uuid ne $user->uuid ) {
return $ws->send(
encode_json( { error => 'You are not the owner of this pj.' } ) );
}
$session->{pj} = $pj;
}
1;

View File

@ -0,0 +1,28 @@
package LasTres::Controller::Websocket::InputPackets;
use v5.36.0;
use strict;
use warnings;
use Moo;
use Module::Pluggable search_path => ['LasTres::Controller::Websocket::InputPacket'],
instantiate => 'new',
on_require_error => sub ($plugin, $error) {
die $error;
};
has hash => (
is => 'lazy',
);
sub _build_hash($self) {
my %hash;
for my $packet ($self->plugins()) {
$hash{$packet->identifier} = $packet;
}
return \%hash;
}
1;

View File

@ -0,0 +1,22 @@
package LasTres::Controller::Websocket::OutputPacket;
use v5.36.0;
use strict;
use warnings;
use Moo::Role;
requires qw/new identifier data/;
sub send ( $self, $ws ) {
return $ws->send(
encode_json(
{
command => $self->identifier,
data => $self->data
}
)
);
}
1;

View File

@ -0,0 +1,51 @@
package LasTres::Controller::Websocket::OutputPacket::Info;
use v5.36.0;
use strict;
use warnings;
use feature 'signatures';
with 'LasTres::Controller::Websocket::OutputPacket';
has clear => (
is => 'rw',
);
has team_pjs => (
is => 'rw',
);
has location_data => (
is => 'rw',
);
sub identifier {
return 'info';
}
sub data($self) {
my $clear = $self->clear;
my $team_pjs = $self->team_pjs;
my $location_data = $self->location_data;
return {
(
(defined $clear)
? (clear => $clear)
: ()
),
(
(defined $team_pjs)
? (team_pjs => $team_pjs)
: ()
),
(
(defined $location_data)
? (location_data => $location_data)
: ()
)
};
}
1;

30
lib/LasTres/Redis.pm Normal file
View File

@ -0,0 +1,30 @@
package LasTres::Redis;
use v5.36.0;
use strict;
use warnings;
use feature 'signatures';
use parent 'Redis';
use LasTres::Schema;
our $VERSION = $LasTres::Schema::VERSION;
{
my $cached_redis;
sub new {
my $class = shift;
if (!defined $cached_redis) {
$cached_redis = $class->SUPER::new(@_);
}
return $cached_redis;
}
}
sub prefix {
return "LasTres::Redis::$VERSION";
}
1;

View File

@ -1,5 +1,5 @@
package LasTres::Schema;
our $VERSION = 3;
our $VERSION = 4;
use v5.36.0;
@ -14,7 +14,9 @@ use parent 'DBIx::Class::Schema';
__PACKAGE__->load_namespaces();
my $schema;
sub Schema($class) {
if (!defined $schema) {
my $app = LasTres->new;
my $config = $app->{config};
my $database_config = $config->{database};
@ -35,6 +37,8 @@ sub Schema($class) {
$dsn .= ";port=$port";
}
# Undef is perfectly fine for username and password.
return $class->connect($dsn, $user, $password);
$schema = $class->connect($dsn, $user, $password, {auto_savepoint => 1});
}
return $schema;
}
1;

View File

@ -5,8 +5,16 @@ use v5.36.0;
use strict;
use warnings;
use feature 'signatures';
use parent 'DBIx::Class::Core';
use Data::Dumper;
use LasTres::Schema;
use Moo;
__PACKAGE__->table('player_pjs');
__PACKAGE__->add_columns(
@ -83,12 +91,23 @@ __PACKAGE__->add_columns(
inventory => {
data_type => 'uuid',
is_nullable => 0,
},
health => {
data_type => 'integer',
accessor => '_health',
is_nullable => 0,
},
mana => {
data_type => 'integer',
accessor => '_mana',
is_nullable => 0,
}
);
__PACKAGE__->set_primary_key('uuid');
__PACKAGE__->has_many('npcs', 'LasTres::Schema::Result::CompanionNPC', 'owner');
__PACKAGE__->has_many( 'npcs', 'LasTres::Schema::Result::CompanionNPC',
'owner' );
__PACKAGE__->belongs_to( 'born_stats', 'LasTres::Schema::Result::Stats' );
__PACKAGE__->belongs_to( 'training_stats', 'LasTres::Schema::Result::Stats' );
__PACKAGE__->belongs_to( 'inventory', 'LasTres::Schema::Result::Inventory' );
@ -97,4 +116,89 @@ __PACKAGE__->belongs_to('spells', 'LasTres::Schema::Result::SkillLikeList');
__PACKAGE__->belongs_to( 'equipment', 'LasTres::Schema::Result::Equipment' );
__PACKAGE__->belongs_to( 'team', 'LasTres::Schema::Result::Team' );
__PACKAGE__->belongs_to( 'owner', 'LasTres::Schema::Result::Player' );
my $columns = __PACKAGE__->columns_info;
for my $column_name (keys %$columns) {
my $column = $columns->{$column_name};
my $is_nullable = $column->{is_nullable};
$is_nullable //= 0;
my $required = !$is_nullable;
has $column_name => (
is => 'rw',
required => $required,
accessor => "_moo_$column_name",
);
}
sub max_health ($self) {
my $races = LasTres::Races->new;
my $race = $races->hash_playable->{$self->race};
my $health_base_race = $race->base_stats->health;
my $health_born = $self->born_stats->health;
my $health_training = $self->training_stats->health;
my $health_mix =
2 * $health_base_race + $health_born + ( $health_training / 4 );
my $health_scaled = ( ( $health_mix * $self->level ) / 100 );
return int($health_scaled + $self->level + 10);
}
sub max_mana ($self) {
my $races = LasTres::Races->new;
my $race = $races->hash_playable->{$self->race};
my $mana_base_race = $race->base_stats->mana;
my $mana_born = $self->born_stats->mana;
my $mana_training = $self->training_stats->mana;
my $mana_mix =
2 * $mana_base_race + $mana_born + ( $mana_training / 4 );
my $mana_scaled = ( ( $mana_mix * $self->level ) / 100 );
return int($mana_scaled + $self->level + 10);
}
sub health {
my $self = shift;
my $health_to_set = shift;
my $schema = LasTres::Schema->Schema;
$schema->txn_do(sub {
if (defined $health_to_set) {
$self->_health($health_to_set);
$self->update;
}
my $health = $self->_health;
if ($health < 0) {
$self->_health(0);
$self->update;
}
if ($health > $self->max_health) {
$self->_health($self->max_health);
$self->update;
}
});
return $self->_health;
}
sub mana {
my $self = shift;
my $mana_to_set = shift;
my $schema = LasTres::Schema->Schema;
$schema->txn_do(sub {
if (defined $mana_to_set) {
$self->_mana($mana_to_set);
$self->update;
}
my $mana = $self->_mana;
if ($mana < 0) {
$self->_mana(0);
$self->update;
}
if ($mana > $self->max_mana) {
$self->_mana($self->max_mana);
$self->update;
}
});
return $self->_mana;
}
sub level ($self) {
return $self->experience**( 1 / 3 );
}
1;

View File

@ -26,22 +26,43 @@ __PACKAGE__->add_columns(
planet => {
data_type => 'text',
is_nullable => 0,
accessor => '_planet',
},
super_area => {
data_type => 'text',
is_nullable => 0,
accessor => '_super_area',
},
area => {
data_type => 'text',
is_nullable => 0,
accessor => '_area',
},
location => {
data_type => 'text',
is_nullable => 0,
accessor => '_location',
},
);
sub location {
my $self = shift;
my $location = shift;
if (defined $location) {
$self->_location($location->identifier);
my $area = $location->parent;
$self->_area($area->identifier);
my $super_area = $area->parent;
$self->_super_area($super_area->identifier)
my $planet = $super_area->parent;
$self->_planet($planet->identifier);
}
my $location = $self->_location;
my $area = $self->_area;
my $super_area = $self->_super_area;
my $planet = $self->_planet;
return $location;
}
__PACKAGE__->set_primary_key('uuid');
__PACKAGE__->add_unique_constraint(u_name => ['name']);
__PACKAGE__->has_many('members', 'LasTres::Schema::Result::PJ', 'team');

View File

@ -1,7 +1,8 @@
body {
margin: 0px;
padding: 0px;
min-height: 100%; }
min-height: 100%;
background: ghostwhite; }
body div.width-max-content {
width: max-content; }
body div#game-container {
@ -65,6 +66,7 @@ body {
box-shadow: 3px 1px 3px 3px #333;
color: blue;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
@ -72,6 +74,15 @@ body {
body div.pj-selection-menu div.pj-selection-menu-container div.pj-list a:hover {
color: yellow;
background: gray; }
body div.pj-selection-menu div.pj-selection-menu-container div.pj-list a label {
width: 90%; }
body div.pj-selection-menu div.pj-selection-menu-container div.pj-list a label div.bar {
width: 100%;
height: 1em;
border: solid 1px black; }
body div.pj-selection-menu div.pj-selection-menu-container div.pj-list a label div.bar div.filled {
background: lightgreen;
height: 100%; }
body div.login-container {
display: flex;
justify-content: center;

View File

@ -2,6 +2,7 @@ body {
margin: 0px;
padding: 0px;
min-height: 100%;
background: ghostwhite;
div.width-max-content {
width: max-content;
}
@ -73,6 +74,7 @@ body {
box-shadow: 3px 1px 3px 3px #333;
color: blue;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
@ -81,6 +83,18 @@ body {
background: gray;
}
text-decoration: none;
label {
width: 90%;
div.bar {
width: 100%;
height: 1em;
border: solid 1px black;
div.filled {
background: lightgreen;
height: 100%;
}
}
}
}
}
}

View File

@ -96,7 +96,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
\************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Game)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _lastres_components_upper_panel__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lastres/components/upper-panel */ \"./js-src/components/upper-panel.tsx\");\n/* harmony import */ var _lastres_components_bottom_panel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @lastres/components/bottom-panel */ \"./js-src/components/bottom-panel.tsx\");\n/* harmony import */ var _lastres_components_pj_selection_menu__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @lastres/components/pj-selection-menu */ \"./js-src/components/pj-selection-menu.tsx\");\n\n\n\n\nfunction Game(props) {\n if (props.selectedPJ === null) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_pj_selection_menu__WEBPACK_IMPORTED_MODULE_3__[\"default\"], { setSelectedPJ: props.setSelectedPJ, userWantsToCreatePJ: props.userWantsToCreatePJ, setUserWantsToCreatePJ: props.setUserWantsToCreatePJ, error: props.error, setError: props.setError })));\n }\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_upper_panel__WEBPACK_IMPORTED_MODULE_1__[\"default\"], null),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_bottom_panel__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null)));\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/components/game.tsx?");
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Game)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _lastres_components_upper_panel__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lastres/components/upper-panel */ \"./js-src/components/upper-panel.tsx\");\n/* harmony import */ var _lastres_components_bottom_panel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @lastres/components/bottom-panel */ \"./js-src/components/bottom-panel.tsx\");\n/* harmony import */ var _lastres_components_pj_selection_menu__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @lastres/components/pj-selection-menu */ \"./js-src/components/pj-selection-menu.tsx\");\n\n\n\n\nfunction Game(props) {\n if (props.selectedPJ === null) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_pj_selection_menu__WEBPACK_IMPORTED_MODULE_3__[\"default\"], { setSelectedPJ: props.setSelectedPJ, userWantsToCreatePJ: props.userWantsToCreatePJ, setUserWantsToCreatePJ: props.setUserWantsToCreatePJ, error: props.error, setError: props.setError })));\n }\n window.setTimeout(() => {\n const locationProtocol = window.location.protocol;\n if (locationProtocol == null) {\n return;\n }\n const protocol = locationProtocol.match(/https:/) != null ? 'wss' : 'ws';\n const webSocket = new WebSocket(`${protocol}://${window.location.host}/ws`);\n webSocket.onopen = () => {\n webSocket.send(JSON.stringify({ hola: \"mundo\" }));\n };\n }, 1);\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_upper_panel__WEBPACK_IMPORTED_MODULE_1__[\"default\"], null),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_bottom_panel__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null)));\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/components/game.tsx?");
/***/ }),
@ -130,13 +130,33 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
/***/ }),
/***/ "./js-src/components/pj-health-like-bar.tsx":
/*!**************************************************!*\
!*** ./js-src/components/pj-health-like-bar.tsx ***!
\**************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ PJHealthLikeBar)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n\nfunction PJHealthLikeBar(props) {\n const percentage = ((props.value / props.max) * 100) + '%';\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"bar\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"filled\", style: { width: percentage } })));\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/components/pj-health-like-bar.tsx?");
/***/ }),
/***/ "./js-src/components/pj-list-selection.tsx":
/*!*************************************************!*\
!*** ./js-src/components/pj-list-selection.tsx ***!
\*************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ PJListSelection)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _lastres_components_pj_health_like_bar__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lastres/components/pj-health-like-bar */ \"./js-src/components/pj-health-like-bar.tsx\");\n\n\nfunction PJListSelection(props) {\n const pjs = props.pjs;\n if (pjs === null) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null));\n }\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, pjs.map((item, i) => react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { onClick: () => {\n props.setSelectedPJ(item);\n }, href: \"#\", key: i },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"span\", null, item.full_name),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"span\", null, item.short_name),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"span\", null, item.nick),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"label\", null,\n \"Salud\",\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_pj_health_like_bar__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { value: item.health, max: item.max_health })),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"label\", null,\n \"Mana\",\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_pj_health_like_bar__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { value: item.mana, max: item.max_mana }))))));\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/components/pj-list-selection.tsx?");
/***/ }),
/***/ "./js-src/components/pj-selection-menu.tsx":
/*!*************************************************!*\
!*** ./js-src/components/pj-selection-menu.tsx ***!
\*************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ PJSelectionMenu)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _lastres_components_pj_creation_menu__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lastres/components/pj-creation-menu */ \"./js-src/components/pj-creation-menu.tsx\");\n\n\nfunction PJSelectionMenu(props) {\n const createPJ = () => {\n props.setUserWantsToCreatePJ(true);\n };\n if (props.userWantsToCreatePJ) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_pj_creation_menu__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { setSelectedPJ: props.setSelectedPJ, setUserWantsToCreatePJ: props.setUserWantsToCreatePJ, error: props.error, setError: props.setError }));\n }\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"pj-selection-menu\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"pj-selection-menu-container\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"h1\", null, \"L3TDE\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"h2\", null, \"Selecciona un Personaje\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"pj-list\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { onClick: createPJ, href: \"#\" }, \"Crear un nuevo personaje\")))));\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/components/pj-selection-menu.tsx?");
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ PJSelectionMenu)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _lastres_components_pj_creation_menu__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lastres/components/pj-creation-menu */ \"./js-src/components/pj-creation-menu.tsx\");\n/* harmony import */ var _lastres_components_pj_list_selection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @lastres/components/pj-list-selection */ \"./js-src/components/pj-list-selection.tsx\");\n/* harmony import */ var _lastres_pj__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @lastres/pj */ \"./js-src/pj.ts\");\n\n\n\n\nfunction PJSelectionMenu(props) {\n const [pjs, setPJs] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const createPJ = () => {\n props.setUserWantsToCreatePJ(true);\n };\n if (props.userWantsToCreatePJ) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_pj_creation_menu__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { setSelectedPJ: props.setSelectedPJ, setUserWantsToCreatePJ: props.setUserWantsToCreatePJ, error: props.error, setError: props.setError }));\n }\n if (pjs === null) {\n (0,_lastres_pj__WEBPACK_IMPORTED_MODULE_3__.fetchMyPjs)(props.setError)\n .then((pjs) => { setPJs(pjs); });\n }\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"pj-selection-menu\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"pj-selection-menu-container\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"h1\", null, \"L3TDE\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"h2\", null, \"Selecciona un Personaje\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"pj-list\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { onClick: createPJ, href: \"#\" }, \"Crear un nuevo personaje\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_pj_list_selection__WEBPACK_IMPORTED_MODULE_2__[\"default\"], { setSelectedPJ: props.setSelectedPJ, pjs: pjs })))));\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/components/pj-selection-menu.tsx?");
/***/ }),
@ -178,6 +198,16 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var reac
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ checkLogin: () => (/* binding */ checkLogin)\n/* harmony export */ });\nfunction checkLogin(setError, setIsLoggedIn) {\n fetch('/player/check_login', {\n method: 'POST',\n mode: 'same-origin',\n cache: 'no-cache'\n }).then(async (response) => {\n const data = await response.json();\n if (data.is_login === 1) {\n setIsLoggedIn(true);\n }\n }).catch((error) => {\n console.log(error);\n });\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/login.ts?");
/***/ }),
/***/ "./js-src/pj.ts":
/*!**********************!*\
!*** ./js-src/pj.ts ***!
\**********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ fetchMyPjs: () => (/* binding */ fetchMyPjs)\n/* harmony export */ });\nasync function fetchMyPjs(setError) {\n const response = await fetch('/my/pjs', {\n method: 'GET',\n mode: 'same-origin',\n cache: 'no-cache'\n }).catch((error) => {\n console.log(error);\n setError('Error recuperando tus pjs');\n });\n if (response === undefined) {\n return [];\n }\n const statusCode = response.status;\n const data = await response.json();\n if (statusCode !== 200) {\n setError(data.error);\n return [];\n }\n return data;\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/pj.ts?");
/***/ })
/******/ });

View File

@ -28,8 +28,12 @@ my $dh = DH->new(
$dh->prepare_deploy;
eval {
$dh->prepare_upgrade;
$dh->prepare_upgrade({
from_version => $LasTres::Schema::VERSION - 1,
to_version => $LasTres::Schema::VERSION
});
};
if ($@) {
print "$@\n";
$dh->prepare_install;
}