Adding initial commit.
This commit is contained in:
parent
b4fc8175db
commit
1473306fcc
24
Build.PL
Executable file
24
Build.PL
Executable file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env perl
|
||||
use Module::Build;
|
||||
|
||||
my $home = $ENV{HOME};
|
||||
|
||||
my $build = Module::Build->new(
|
||||
module_name => 'LasTres',
|
||||
license => 'AGPLv3',
|
||||
dist_author => 'Sergio Iglesias <contact@owlcode.tech>',
|
||||
dist_abstract => 'Juego de L3TDE.',
|
||||
requires => {
|
||||
'Mojolicious' => 0,
|
||||
'Moo' => 0,
|
||||
'Params::ValidationCompiler' => 0,
|
||||
'Types::Standard' => 0,
|
||||
'Crypt::URandom' => 0,
|
||||
'Crypt::Bcrypt' => 0,
|
||||
'DBIx::Class' => 0,
|
||||
'DBIx::Class::DeploymentHandler' => 0,
|
||||
'UUID::URandom' => 0,
|
||||
'Module::Pluggable' => 0,
|
||||
},
|
||||
);
|
||||
$build->create_build_script;
|
5
babel.config.json
Normal file
5
babel.config.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/preset-react"
|
||||
]
|
||||
}
|
8
build_styles.sh
Normal file
8
build_styles.sh
Normal file
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
if which sassc &> /dev/null; then
|
||||
sassc public/css/styles.scss > public/css/styles.css
|
||||
else
|
||||
echo "No sassc"
|
||||
exit 1
|
||||
fi
|
1
dbicdh/PostgreSQL/deploy/1/000-deploy.sql
Normal file
1
dbicdh/PostgreSQL/deploy/1/000-deploy.sql
Normal file
@ -0,0 +1 @@
|
||||
CREATE EXTENSION "uuid-ossp";
|
18
dbicdh/PostgreSQL/deploy/1/001-auto-__VERSION.sql
Normal file
18
dbicdh/PostgreSQL/deploy/1/001-auto-__VERSION.sql
Normal file
@ -0,0 +1,18 @@
|
||||
--
|
||||
-- Created by SQL::Translator::Producer::PostgreSQL
|
||||
-- Created on Mon May 29 12:19:47 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")
|
||||
);
|
||||
|
||||
;
|
252
dbicdh/PostgreSQL/deploy/1/001-auto.sql
Normal file
252
dbicdh/PostgreSQL/deploy/1/001-auto.sql
Normal file
@ -0,0 +1,252 @@
|
||||
--
|
||||
-- Created by SQL::Translator::Producer::PostgreSQL
|
||||
-- Created on Mon May 29 12:19:46 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,
|
||||
"charisma" 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 NOT NULL,
|
||||
"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,
|
||||
"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_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_stats" on "player_pjs" ("stats");
|
||||
CREATE INDEX "player_pjs_idx_team" on "player_pjs" ("team");
|
||||
|
||||
;
|
||||
--
|
||||
-- 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_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") 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_stats" FOREIGN KEY ("stats")
|
||||
REFERENCES "stats" ("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_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;
|
||||
|
||||
;
|
91
dbicdh/_source/deploy/1/001-auto-__VERSION.yml
Normal file
91
dbicdh/_source/deploy/1/001-auto-__VERSION.yml
Normal 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
|
1255
dbicdh/_source/deploy/1/001-auto.yml
Normal file
1255
dbicdh/_source/deploy/1/001-auto.yml
Normal file
File diff suppressed because it is too large
Load Diff
7
js-src/components/bottom-panel.tsx
Normal file
7
js-src/components/bottom-panel.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import * as React from 'react'
|
||||
export default function BottomPanel (): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
}
|
35
js-src/components/game.tsx
Normal file
35
js-src/components/game.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import * as React from 'react'
|
||||
|
||||
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
|
||||
userWantsToCreatePJ: boolean
|
||||
setUserWantsToCreatePJ: (set: boolean) => void
|
||||
error: string | null
|
||||
setError: (set: string | null) => void
|
||||
}
|
||||
|
||||
export default function Game (props: GameProps): JSX.Element {
|
||||
if (props.selectedPJ === null) {
|
||||
return (
|
||||
<>
|
||||
<PJSelectionMenu
|
||||
setSelectedPJ={props.setSelectedPJ}
|
||||
userWantsToCreatePJ={props.userWantsToCreatePJ}
|
||||
setUserWantsToCreatePJ={props.setUserWantsToCreatePJ}
|
||||
error={props.error}
|
||||
setError={props.setError}/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<UpperPanel/>
|
||||
<BottomPanel/>
|
||||
</>
|
||||
)
|
||||
}
|
65
js-src/components/login-page.tsx
Normal file
65
js-src/components/login-page.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import * as React from 'react'
|
||||
|
||||
export interface LoginPageProps {
|
||||
setIsLoggedIn: (set: boolean) => void
|
||||
setIsAskingForRegistration: (set: boolean) => void
|
||||
setError: (set: string | null) => void
|
||||
error: string | null
|
||||
}
|
||||
|
||||
export default function LoginPage (props: LoginPageProps): JSX.Element {
|
||||
const userInputRef = React.useRef<HTMLInputElement>(null)
|
||||
const passwordInputRef = React.useRef<HTMLInputElement>(null)
|
||||
const onUserAsksForRegistration = (): void => {
|
||||
props.setIsAskingForRegistration(true)
|
||||
}
|
||||
const login = (): void => {
|
||||
if (userInputRef.current === null) {
|
||||
return
|
||||
}
|
||||
if (passwordInputRef.current == null) {
|
||||
return
|
||||
}
|
||||
fetch('/player/login', {
|
||||
method: 'POST',
|
||||
mode: 'same-origin',
|
||||
cache: 'no-cache',
|
||||
body: JSON.stringify({
|
||||
username: userInputRef.current.value,
|
||||
password: passwordInputRef.current.value
|
||||
})
|
||||
}).then(async (response) => {
|
||||
const statusCode = response.status
|
||||
const data = await response.json()
|
||||
if (statusCode !== 200) {
|
||||
props.setError(data.error)
|
||||
return
|
||||
}
|
||||
props.setError(null)
|
||||
props.setIsLoggedIn(true)
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="login-container">
|
||||
<div className="login-contained">
|
||||
<h1>Inicia sesión en L3TDE online</h1>
|
||||
{(props.error !== null
|
||||
? (<p style={{ background: 'red' }}>{props.error}</p>)
|
||||
: (<></>)
|
||||
)}
|
||||
<div className="login-form">
|
||||
<input ref={userInputRef} type="text" placeholder="Nombre de usuario"/>
|
||||
<input ref={passwordInputRef} type="password" placeholder="Password"/>
|
||||
<div className="width-max-content align-self-end">
|
||||
<button onClick={login}>Login</button>
|
||||
</div>
|
||||
</div>
|
||||
<p>¿Todavía no tienes cuenta? <a href="#" onClick={onUserAsksForRegistration}>Registrate</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
42
js-src/components/menu-bar.tsx
Normal file
42
js-src/components/menu-bar.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
'use strict'
|
||||
import * as React from 'react'
|
||||
|
||||
export interface MenuBarProps {
|
||||
isLoggedIn: boolean
|
||||
};
|
||||
|
||||
export interface MenuItem {
|
||||
onClick: () => void
|
||||
text: string
|
||||
};
|
||||
|
||||
export default function MenuBar (props: MenuBarProps): JSX.Element {
|
||||
const isLoggedIn = props.isLoggedIn
|
||||
const menuItems: MenuItem[] = []
|
||||
fillMenu(menuItems, isLoggedIn)
|
||||
return (
|
||||
<nav className="menu-bar">
|
||||
{
|
||||
menuItems.map((item, i) => <a key={i} href="#" onClick={item.onClick}>{item.text}</a>)
|
||||
}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
function fillMenu (menuItems: MenuItem[], isLoggedIn: boolean): void {
|
||||
if (isLoggedIn) {
|
||||
menuItems.push({
|
||||
text: 'Cerrar Sesión',
|
||||
onClick: () => { alert('logout') }
|
||||
})
|
||||
return
|
||||
}
|
||||
menuItems.push({
|
||||
text: 'Regístrate',
|
||||
onClick: () => { alert('register') }
|
||||
})
|
||||
menuItems.push({
|
||||
text: 'Inicia Sesión',
|
||||
onClick: () => { alert('login') }
|
||||
})
|
||||
}
|
52
js-src/components/page.tsx
Normal file
52
js-src/components/page.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import LoginPage from '@lastres/components/login-page'
|
||||
import Game from '@lastres/components/game'
|
||||
import { checkLogin } from '@lastres/login'
|
||||
import RegistrationPage from '@lastres/components/registration-page'
|
||||
// import Game from '@lastres/components/game'
|
||||
|
||||
export default function Page (): JSX.Element {
|
||||
const [isLoggedIn, setIsLoggedIn] = React.useState<boolean>(false)
|
||||
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)
|
||||
checkLogin(setError, setIsLoggedIn)
|
||||
if (!isLoggedIn) {
|
||||
return notLoggedRender(setIsLoggedIn, isAskingForRegistration, setIsAskingForRegistration, error, setError)
|
||||
}
|
||||
return (
|
||||
<Game selectedPJ={selectedPJ}
|
||||
setSelectedPJ={setSelectedPJ}
|
||||
userWantsToCreatePJ={userWantsToCreatePJ}
|
||||
setUserWantsToCreatePJ={setUserWantsToCreatePJ}
|
||||
error={error}
|
||||
setError={setError}/>
|
||||
)
|
||||
}
|
||||
|
||||
function notLoggedRender (setIsLoggedIn: (a: boolean) => void,
|
||||
isAskingForRegistration: boolean,
|
||||
setIsAskingForRegistration: (a: boolean) => void,
|
||||
error: string | null,
|
||||
setError: (a: string | null) => void): JSX.Element {
|
||||
if (isAskingForRegistration) {
|
||||
return (
|
||||
<>
|
||||
<RegistrationPage setIsLoggedIn={setIsLoggedIn}
|
||||
setIsAskingForRegistration={setIsAskingForRegistration}
|
||||
error={error}
|
||||
setError={setError}/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<LoginPage setIsLoggedIn={setIsLoggedIn}
|
||||
error={error}
|
||||
setError={setError}
|
||||
setIsAskingForRegistration={setIsAskingForRegistration}/>
|
||||
</>
|
||||
)
|
||||
}
|
75
js-src/components/pj-creation-menu.tsx
Normal file
75
js-src/components/pj-creation-menu.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import * as React from 'react'
|
||||
|
||||
export interface PJCreationMenuProps {
|
||||
error: string | null
|
||||
setSelectedPJ: (set: string | null) => void
|
||||
setUserWantsToCreatePJ: (set: boolean) => void
|
||||
setError: (set: string | null) => void
|
||||
}
|
||||
|
||||
export interface Race {
|
||||
identifier: string;
|
||||
name_selection: string;
|
||||
description: string;
|
||||
}
|
||||
export interface Races {
|
||||
[id: string]: Race
|
||||
}
|
||||
|
||||
export default function PJCreationMenu (props: PJCreationMenuProps): JSX.Element {
|
||||
const longNameInputRef = React.useRef<HTMLInputElement>(null)
|
||||
const shortNameInputRef = React.useRef<HTMLInputElement>(null)
|
||||
const nickInputRef = React.useRef<HTMLInputElement>(null)
|
||||
const raceSelectRef = React.useRef<HTMLSelectElement>(null)
|
||||
const [playableRaces, setPlayableRaces] = React.useState<Races>({})
|
||||
React.useEffect(() => {
|
||||
fetch('/races/playable', {
|
||||
method: 'GET',
|
||||
mode: 'same-origin',
|
||||
cache: 'no-cache'
|
||||
}).then(async (response) => {
|
||||
const data = await response.json()
|
||||
setPlayableRaces(data)
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
props.setError('Imposible conectar al servidor para recibir las razas.')
|
||||
})
|
||||
})
|
||||
return (
|
||||
<>
|
||||
<div className="login-container">
|
||||
<div className="login-contained">
|
||||
<h1>Crea tu personaje.</h1>
|
||||
{(props.error !== null
|
||||
? (<p style={{ background: 'red' }}>{props.error}</p>)
|
||||
: (<></>)
|
||||
)}
|
||||
<div className="login-form">
|
||||
<label>Nombre largo. (Se usará en la historia en situaciones formales)</label>
|
||||
<input ref={longNameInputRef} type="text"/>
|
||||
<label>Nombre corto. (Se usará de forma coloquial)</label>
|
||||
<input ref={shortNameInputRef} type="text"/>
|
||||
<label>Apodo. (Se usará en las conversaciones más distendidas)</label>
|
||||
<input ref={nickInputRef} type="text"/>
|
||||
<label>Raza. (Determina tu localización inicial y tus estadísticas)</label>
|
||||
<select ref={raceSelectRef}>
|
||||
{
|
||||
Object.keys(playableRaces)
|
||||
.map(
|
||||
(item, i) => {
|
||||
return <option key={i} value={playableRaces[item].identifier}>
|
||||
{`${playableRaces[item].name_selection} (${playableRaces[item].description})`}
|
||||
</option>
|
||||
}
|
||||
)
|
||||
}
|
||||
</select>
|
||||
<div className="width-max-content align-self-end">
|
||||
<button>Crear Personaje</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
36
js-src/components/pj-selection-menu.tsx
Normal file
36
js-src/components/pj-selection-menu.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import * as React from 'react'
|
||||
import PJCreationMenu from '@lastres/components/pj-creation-menu'
|
||||
|
||||
export interface PJSelectionMenuProps {
|
||||
setSelectedPJ: (set: string | null) => void
|
||||
setUserWantsToCreatePJ: (set: boolean) => void
|
||||
userWantsToCreatePJ: boolean
|
||||
error: string | null
|
||||
setError: (set: string | null) => void
|
||||
}
|
||||
|
||||
export default function PJSelectionMenu (props: PJSelectionMenuProps): JSX.Element {
|
||||
const createPJ = (): void => {
|
||||
props.setUserWantsToCreatePJ(true)
|
||||
}
|
||||
if (props.userWantsToCreatePJ) {
|
||||
return (
|
||||
<PJCreationMenu
|
||||
setSelectedPJ={props.setSelectedPJ}
|
||||
setUserWantsToCreatePJ={props.setUserWantsToCreatePJ}
|
||||
error={props.error}
|
||||
setError={props.setError}/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className="pj-selection-menu">
|
||||
<div className="pj-selection-menu-container">
|
||||
<h1>L3TDE</h1>
|
||||
<h2>Selecciona un Personaje</h2>
|
||||
<div className="pj-list">
|
||||
<a onClick={createPJ} href="#">Crear un nuevo personaje</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
76
js-src/components/registration-page.tsx
Normal file
76
js-src/components/registration-page.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import * as React from 'react'
|
||||
|
||||
export interface RegistrationPageProps {
|
||||
setIsLoggedIn: (set: boolean) => void
|
||||
setIsAskingForRegistration: (set: boolean) => void
|
||||
error: string | null
|
||||
setError: (a: string|null) => void
|
||||
}
|
||||
|
||||
export default function RegistrationPage (props: RegistrationPageProps): JSX.Element {
|
||||
const userInputRef = React.useRef<HTMLInputElement>(null)
|
||||
const passwordInputRef = React.useRef<HTMLInputElement>(null)
|
||||
const repeatPasswordInputRef = React.useRef<HTMLInputElement>(null)
|
||||
const emailInputRef = React.useRef<HTMLInputElement>(null)
|
||||
const onUserAsksForLogin = (): void => {
|
||||
props.setIsAskingForRegistration(false)
|
||||
}
|
||||
function registerUser (): void {
|
||||
if (userInputRef.current === null) {
|
||||
return
|
||||
}
|
||||
if (passwordInputRef.current === null) {
|
||||
return
|
||||
}
|
||||
if (repeatPasswordInputRef.current === null) {
|
||||
return
|
||||
}
|
||||
if (emailInputRef.current === null) {
|
||||
return
|
||||
}
|
||||
fetch('/player/register', {
|
||||
method: 'POST',
|
||||
mode: 'same-origin',
|
||||
cache: 'no-cache',
|
||||
body: JSON.stringify({
|
||||
username: userInputRef.current.value,
|
||||
password: passwordInputRef.current.value,
|
||||
repeat_password: repeatPasswordInputRef.current.value,
|
||||
email: emailInputRef.current.value
|
||||
})
|
||||
}).then(async (response) => {
|
||||
const statusCode = response.status
|
||||
const data = await response.json()
|
||||
if (statusCode !== 200) {
|
||||
props.setError(data.error)
|
||||
return
|
||||
}
|
||||
props.setError(null)
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="login-container">
|
||||
<div className="login-contained">
|
||||
<h1>Inicia sesión en L3TDE online</h1>
|
||||
{(props.error !== null
|
||||
? (<p style={{ background: 'red' }}>{props.error}</p>)
|
||||
: (<></>)
|
||||
)}
|
||||
<div className="login-form">
|
||||
<input ref={userInputRef} type="text" placeholder="Nombre de usuario."/>
|
||||
<input ref={passwordInputRef} type="password" placeholder="Password."/>
|
||||
<input ref={repeatPasswordInputRef} type="password" placeholder="Repeat password."/>
|
||||
<input ref={emailInputRef} type="text" placeholder="Email."/>
|
||||
<div className="width-max-content align-self-end">
|
||||
<button onClick={registerUser}>Registrate</button>
|
||||
</div>
|
||||
</div>
|
||||
<p>¿Ya tienes cuenta? <a href="#" onClick={onUserAsksForLogin}>Inicia sesión</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
7
js-src/components/upper-panel.tsx
Normal file
7
js-src/components/upper-panel.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import * as React from 'react'
|
||||
export default function UpperPanel (): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
}
|
11
js-src/index.tsx
Normal file
11
js-src/index.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
'use strict'
|
||||
import * as React from 'react'
|
||||
import * as ReactDOMClient from 'react-dom/client'
|
||||
|
||||
import Page from '@lastres/components/page'
|
||||
|
||||
const gameContainer = document.querySelector('#game-container')
|
||||
if (gameContainer !== null) {
|
||||
const root = ReactDOMClient.createRoot(gameContainer)
|
||||
root.render(<Page/>)
|
||||
}
|
15
js-src/login.ts
Normal file
15
js-src/login.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export function checkLogin (setError: (set: string | null) => void,
|
||||
setIsLoggedIn: (set: boolean) => void): void {
|
||||
fetch('/player/check_login', {
|
||||
method: 'POST',
|
||||
mode: 'same-origin',
|
||||
cache: 'no-cache'
|
||||
}).then(async (response) => {
|
||||
const data = await response.json()
|
||||
if (data.is_login === 1) {
|
||||
setIsLoggedIn(true)
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
48
lib/LasTres.pm
Normal file
48
lib/LasTres.pm
Normal file
@ -0,0 +1,48 @@
|
||||
package LasTres;
|
||||
|
||||
use Mojo::Base 'Mojolicious', -signatures;
|
||||
|
||||
# This method will run once at server start
|
||||
sub startup ($self) {
|
||||
|
||||
# Load configuration from config file
|
||||
my $config = $self->plugin('NotYAMLConfig');
|
||||
|
||||
# Configure the application
|
||||
$self->secrets( $config->{secrets} );
|
||||
|
||||
# Router
|
||||
my $r = $self->routes;
|
||||
|
||||
$self->helper(user => sub($self) {
|
||||
require LasTres::DAO::Players;
|
||||
my $result_set_players = LasTres::DAO::Players->ResultSet;
|
||||
my $uuid = $self->session->{uuid};
|
||||
if (!defined $uuid) {
|
||||
return;
|
||||
}
|
||||
my @players = $result_set_players->search({
|
||||
uuid => $uuid,
|
||||
});
|
||||
my $player = $players[0];
|
||||
if (!defined $player) {
|
||||
delete $self->session->{uuid};
|
||||
return;
|
||||
}
|
||||
return $player;
|
||||
});
|
||||
|
||||
my $sessions = Mojolicious::Sessions->new;
|
||||
$sessions->cookie_name('LasTres');
|
||||
$sessions->default_expiration(86400);
|
||||
|
||||
# Normal route to controller
|
||||
$r->get('/')->to('Root#index');
|
||||
$r->post('/player/register')->to('Player#register');
|
||||
$r->post('/player/login')->to('Player#login');
|
||||
$r->post('/player/check_login')->to('Player#check_login');
|
||||
$r->post('/pj/create')->to('PJ#create');;
|
||||
$r->websocket('/ws')->to('Websocket#ws');
|
||||
}
|
||||
|
||||
1;
|
11
lib/LasTres/Area.pm
Normal file
11
lib/LasTres/Area.pm
Normal file
@ -0,0 +1,11 @@
|
||||
package LasTres::Area;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Moo::Role;
|
||||
|
||||
requires qw/identifier locations name description parent/;
|
||||
1;
|
11
lib/LasTres/Controller/Example.pm
Normal file
11
lib/LasTres/Controller/Example.pm
Normal file
@ -0,0 +1,11 @@
|
||||
package LasTres::Controller::Example;
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
|
||||
# This action will render a template
|
||||
sub welcome ($self) {
|
||||
|
||||
# Render template "example/welcome.html.ep" with message
|
||||
$self->render(msg => 'Welcome to the Mojolicious real-time web framework!');
|
||||
}
|
||||
|
||||
1;
|
57
lib/LasTres/Controller/PJ.pm
Normal file
57
lib/LasTres/Controller/PJ.pm
Normal file
@ -0,0 +1,57 @@
|
||||
package LasTres::Controller::PJ;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use UUID::URandom qw/create_uuid_string/;
|
||||
|
||||
use LasTres::DAO::Players;
|
||||
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
|
||||
my $result_set_pjs = LasTres::DAO::PJ->ResultSet;
|
||||
|
||||
sub create($self) {
|
||||
my %params = %{ $self->req->json };
|
||||
my $user = $self->user;
|
||||
if (!defined $user) {
|
||||
return $self->render(
|
||||
status => 401,
|
||||
json => { error => 'You must login first.' }
|
||||
);
|
||||
}
|
||||
my @pjs = $user->pjs;
|
||||
if (scalar @pjs >= 0) {
|
||||
return $self->render(
|
||||
status => 401,
|
||||
json => { error => 'You reached the limit of free pjs, delete one or update to premium.', }
|
||||
);
|
||||
}
|
||||
my $uuid = create_uuid_string();
|
||||
my $owner = $user;
|
||||
my $full_name = $param{full_name};
|
||||
if (!defined $full_name || !length $full_name > 3) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => { error => 'The full_name is too short.', }
|
||||
);
|
||||
}
|
||||
my $short_name = $param{full_name};
|
||||
if (!defined $short_name || !length $full_name > 3) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => { error => 'The short_name is too short.', }
|
||||
);
|
||||
}
|
||||
my $nick = $param{'nick'};
|
||||
if (!defined $nick || !length $nick > 0) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => { error => 'You must set a nick.', }
|
||||
);
|
||||
}
|
||||
my $race = $param{'race'};
|
||||
}
|
||||
1;
|
177
lib/LasTres/Controller/Player.pm
Normal file
177
lib/LasTres/Controller/Player.pm
Normal file
@ -0,0 +1,177 @@
|
||||
package LasTres::Controller::Player;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Crypt::URandom qw/urandom/;
|
||||
use Crypt::Bcrypt qw/bcrypt bcrypt_check/;
|
||||
use UUID::URandom qw/create_uuid_string/;
|
||||
|
||||
use LasTres::DAO::Players;
|
||||
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
|
||||
my $result_set_players = LasTres::DAO::Players->ResultSet;
|
||||
|
||||
sub check_login($self) {
|
||||
my $user = $self->user;
|
||||
my $return_hash = {
|
||||
is_login => 0,
|
||||
is_verified => 0,
|
||||
};
|
||||
if (!defined $user) {
|
||||
return $self->render(status => 200, json => $return_hash);
|
||||
}
|
||||
$return_hash->{is_login} = 1;
|
||||
if ($user->verified) {
|
||||
$return_hash->{is_verified} = 1;
|
||||
}
|
||||
return $self->render(status => 200, json => $return_hash);
|
||||
}
|
||||
|
||||
sub login ($self) {
|
||||
my %params = %{ $self->req->json };
|
||||
my $username = $params{username};
|
||||
my $password = $params{password};
|
||||
my @players = $result_set_players->search({
|
||||
-or => [
|
||||
username => $username,
|
||||
email => $username,
|
||||
]
|
||||
});
|
||||
my $player = $players[0];
|
||||
if (!defined $player) {
|
||||
return $self->return_incorrect_username_or_password();
|
||||
}
|
||||
if (!bcrypt_check($password, $player->encrypted_password)) {
|
||||
return $self->return_incorrect_username_or_password();
|
||||
}
|
||||
$self->session->{uuid} = $player->uuid;
|
||||
return $self->render(
|
||||
status => 200,
|
||||
json => {
|
||||
info => 'Login sucessful',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub return_incorrect_username_or_password($self) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => {
|
||||
error => 'Incorrect username or password.',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub register ($self) {
|
||||
my %params = %{ $self->req->json };
|
||||
my $username = $params{username};
|
||||
my $password = $params{password};
|
||||
my $repeat_password = $params{repeat_password};
|
||||
my $email = $params{email};
|
||||
if ( !defined $username || length $username < 5 ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => { error => 'Username too short, minimum 5 characters.', },
|
||||
);
|
||||
}
|
||||
if ($username =~ /@/) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => { error => 'The character @ is forbidden in usernames.', },
|
||||
);
|
||||
}
|
||||
if ( !defined $email || $email !~ /@/ ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => { error => 'This is not a valid mail.', },
|
||||
);
|
||||
}
|
||||
if ( $password ne $repeat_password ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => { error => 'Passwords do not match.' }
|
||||
);
|
||||
}
|
||||
if ( $password =~ /^\d+$/ ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => {
|
||||
error => 'Numeric only password are too unsafe to be allowed.'
|
||||
}
|
||||
);
|
||||
}
|
||||
if ( length $password < 12 ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => {
|
||||
error => 'The password should be at least 12 characters long.'
|
||||
}
|
||||
);
|
||||
}
|
||||
my $new_salt = urandom(16);
|
||||
my $encrypted_password = bcrypt $password, '2b', 12, $new_salt;
|
||||
my $uuid = create_uuid_string();
|
||||
eval {
|
||||
$result_set_players->create(
|
||||
{
|
||||
uuid => $uuid,
|
||||
username => $username,
|
||||
encrypted_password => $encrypted_password,
|
||||
email => $email,
|
||||
verified => 0,
|
||||
}
|
||||
);
|
||||
};
|
||||
if ($@) {
|
||||
if ( $@ =~ /Key \((.*?)\)=\((.*?)\) already exists\./ ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => {
|
||||
error => "The key $1 ($2) already exists in the database.",
|
||||
}
|
||||
);
|
||||
}
|
||||
say STDERR $@;
|
||||
return $self->render(
|
||||
status => 500,
|
||||
json => {
|
||||
error => 'Unhandled database error.',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
my @players = $result_set_players->search({
|
||||
uuid => $uuid,
|
||||
});
|
||||
my $player = $players[0];
|
||||
|
||||
if (!defined $player) {
|
||||
return $self->render(
|
||||
status => 500,
|
||||
json => {
|
||||
error => 'Unknown error creating entity player.',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
undef($@);
|
||||
|
||||
$self->session->{uuid} = $player->uuid;
|
||||
|
||||
return $self->render(
|
||||
status => 200,
|
||||
json => {
|
||||
user => {
|
||||
username => $username,
|
||||
email => $email,
|
||||
verified => 0,
|
||||
},
|
||||
info => 'User sucessfully created',
|
||||
}
|
||||
);
|
||||
}
|
||||
1;
|
13
lib/LasTres/Controller/Root.pm
Normal file
13
lib/LasTres/Controller/Root.pm
Normal file
@ -0,0 +1,13 @@
|
||||
package LasTres::Controller::Root;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
|
||||
sub index($self) {
|
||||
return $self->render;
|
||||
}
|
||||
1;
|
32
lib/LasTres/Controller/Websocket.pm
Normal file
32
lib/LasTres/Controller/Websocket.pm
Normal file
@ -0,0 +1,32 @@
|
||||
package LasTres::Controller::Websocket;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
|
||||
sub ws($self) {
|
||||
my $user = $self->user;
|
||||
if (!defined $user) {
|
||||
return $self->render(
|
||||
status => 401,
|
||||
json => {
|
||||
error => 'You are not logged in.',
|
||||
}
|
||||
);
|
||||
}
|
||||
if (!$user->verified) {
|
||||
return $self->render(
|
||||
status => 401,
|
||||
json => {
|
||||
error => 'Your user is not verified.',
|
||||
}
|
||||
);
|
||||
}
|
||||
$self->on(json => sub($self, $hash) {
|
||||
$self->_handle_packet($user, $hash);
|
||||
});
|
||||
}
|
||||
1;
|
23
lib/LasTres/DAO/Players.pm
Normal file
23
lib/LasTres/DAO/Players.pm
Normal file
@ -0,0 +1,23 @@
|
||||
package LasTres::DAO::Players;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use feature 'signatures';
|
||||
|
||||
use Moo;
|
||||
|
||||
use Params::ValidationCompiler qw/validation_for/;
|
||||
use Types::Standard qw/Str Bool/;
|
||||
|
||||
use LasTres::Schema;
|
||||
|
||||
my $schema = LasTres::Schema->Schema;
|
||||
my $result_set = $schema->resultset('Player');
|
||||
|
||||
sub ResultSet {
|
||||
return $result_set;
|
||||
}
|
||||
1;
|
11
lib/LasTres/Location.pm
Normal file
11
lib/LasTres/Location.pm
Normal file
@ -0,0 +1,11 @@
|
||||
package LasTres::Location;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Moo::Role;
|
||||
|
||||
requires qw/identifier name description parent actions npcs/;
|
||||
1;
|
11
lib/LasTres/Planet.pm
Normal file
11
lib/LasTres/Planet.pm
Normal file
@ -0,0 +1,11 @@
|
||||
package LasTres::Planet;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Moo::Role;
|
||||
|
||||
requires qw/identifier super_areas name description/;
|
||||
1;
|
41
lib/LasTres/Planet/Bahdder.pm
Normal file
41
lib/LasTres/Planet/Bahdder.pm
Normal file
@ -0,0 +1,41 @@
|
||||
package LasTres::Planet::Bahdder;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Moo;
|
||||
|
||||
with 'LasTres::Planet';
|
||||
|
||||
use Module::Pluggable search_path => ['LasTres::Planet::Bahdder'];
|
||||
|
||||
has super_areas => (
|
||||
is => 'lazy'
|
||||
);
|
||||
sub identifier {
|
||||
return 'bahdder',
|
||||
}
|
||||
|
||||
sub _build_super_areas {
|
||||
my $self = shift;
|
||||
my $hash = {};
|
||||
my @super_areas = $self->plugins();
|
||||
for $super_area (@super_areas) {
|
||||
$hash->{$super_area->identifier} = $super_area;
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
sub name {
|
||||
return 'Bahdder';
|
||||
}
|
||||
|
||||
sub description {
|
||||
return 'Archivo de la Patrulla Galáctica: Bahdder es uno de los planetas con mayor población de la galaxia con una población estimada '
|
||||
. 'de tres mil millones de individuos pertenecientes a especies consideradas inteligentes. '
|
||||
. 'Es conocido como el planeta origen de los Yaren; no obstante la presencia de los mismos en este planeta es actualmente simbólica. '
|
||||
. 'Su población actual esta mayormente compuesta por razas locales como los Áldimor. ';
|
||||
}
|
||||
1;
|
47
lib/LasTres/Planet/Bahdder/BosqueDelHeroe.pm
Normal file
47
lib/LasTres/Planet/Bahdder/BosqueDelHeroe.pm
Normal file
@ -0,0 +1,47 @@
|
||||
package LasTres::Planet::Bahdder::BosqueDelHeroe;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Moo;
|
||||
|
||||
with 'LasTres::SuperArea';
|
||||
|
||||
use Module::Pluggable search_path => ['LasTres::Planet::Bahdder::BosqueDelHeroe'];
|
||||
|
||||
has areas => (
|
||||
is => 'lazy'
|
||||
);
|
||||
|
||||
sub identifier {
|
||||
return 'bosque_del_heroe';
|
||||
}
|
||||
|
||||
sub _build_areas {
|
||||
my $self = shift;
|
||||
my $hash = {};
|
||||
my @areas = $self->plugins();
|
||||
for $area (@areas) {
|
||||
$hash->{$area->identifier} = $area;
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
sub name {
|
||||
return 'Bosque del Héroe';
|
||||
}
|
||||
|
||||
sub description {
|
||||
return 'El Bosque del Héroe es el pulmón del planeta Bahdder. '
|
||||
. 'Se cree que solo una pequeña parte de las especies que viven en el mismo han sido catalogadas. '
|
||||
. 'Los áldimor viven en este bosque en armonía con la naturaleza. '
|
||||
. 'En este bosque se encuentra la Torre de hechicería Áldimor, lugar donde los áldimor más talentosos aprenden '
|
||||
. 'a usar la magia para la guerra. ';
|
||||
}
|
||||
|
||||
sub parent {
|
||||
return LasTres::Planet::Bahdder->new;
|
||||
}
|
||||
1;
|
37
lib/LasTres/Planet/Bahdder/BosqueDelHeroe/BosqueDelHeroeI.pm
Normal file
37
lib/LasTres/Planet/Bahdder/BosqueDelHeroe/BosqueDelHeroeI.pm
Normal file
@ -0,0 +1,37 @@
|
||||
package LasTres::Planet::Bahdder::BosqueDelHeroe::BosqueDelHeroeI;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
with 'LasTres::Area';
|
||||
|
||||
use Module::Pluggable search_path => ['LasTres::Planet::Bahdder::BosqueDelHeroe::BosqueDelHeroeI'];
|
||||
|
||||
has locations => (
|
||||
is => 'lazy'
|
||||
);
|
||||
|
||||
sub identifier {
|
||||
return 'bosque_del_heroe_i';
|
||||
}
|
||||
|
||||
sub _build_locations {
|
||||
my $self = shift;
|
||||
my $hash = {};
|
||||
my @locations = $self->plugins();
|
||||
for $location (@locations) {
|
||||
$hash->{$location->identifier} = $location;
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
sub name {
|
||||
return 'Bosque del Héroe (I)';
|
||||
}
|
||||
|
||||
sub parent {
|
||||
return LasTres::Planet::Bahdder::BosqueDelHeroe->new;
|
||||
}
|
||||
1;
|
@ -0,0 +1,35 @@
|
||||
package LasTres::Planet::Bahdder::BosqueDelHeroe::BosqueDelHeroeI::TribuDeLaLima;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
with 'LasTres::Location';
|
||||
|
||||
sub identifier {
|
||||
return 'tribu_de_la_lima';
|
||||
}
|
||||
|
||||
sub name {
|
||||
return 'Tribu de la Lima (Exterior)';
|
||||
}
|
||||
|
||||
sub description {
|
||||
return 'La Tribu de la Lima se siente como un hogar seas o no de aquí. '
|
||||
. 'Las casitas están improvisadas con paja que los aldeanos intercambian con otras tribus. '
|
||||
. 'Los cultivos de Lima están siempre buscando trabajadores, el sueldo es una parte de lo cosechado. ';
|
||||
}
|
||||
|
||||
sub parent {
|
||||
return LasTres::Planet::Bahdder::BosqueDelHeroe::BosqueDelHeroeI->new;
|
||||
}
|
||||
|
||||
sub actions {
|
||||
return [];
|
||||
}
|
||||
|
||||
sub npcs {
|
||||
return [];
|
||||
}
|
||||
1;
|
23
lib/LasTres/Race.pm
Normal file
23
lib/LasTres/Race.pm
Normal file
@ -0,0 +1,23 @@
|
||||
package LasTres::Race;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use feature 'signatures';
|
||||
|
||||
use Moo::Role;
|
||||
|
||||
requires qw/spawn identifier name name_selection description is_playable/;
|
||||
|
||||
sub hash($self) {
|
||||
return {
|
||||
identifier => $self->{identifier},
|
||||
name => $self->{name},
|
||||
name_selection => $self->{name_selection},
|
||||
description => $self->{description},
|
||||
is_playable => $self->{is_playable},
|
||||
}
|
||||
}
|
||||
1;
|
35
lib/LasTres/Race/Aldimor.pm
Normal file
35
lib/LasTres/Race/Aldimor.pm
Normal file
@ -0,0 +1,35 @@
|
||||
package LasTres::Race::Aldimor;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Moo;
|
||||
|
||||
with 'LasTres::Race';
|
||||
|
||||
sub spawn {
|
||||
return LasTres::Planet::Bahdder::BosqueDelHeroe::BosqueDelHeroeI::TribuDeLaLima->new;
|
||||
}
|
||||
|
||||
sub identifier {
|
||||
return 'aldimor';
|
||||
}
|
||||
|
||||
sub name {
|
||||
return 'Aldimor';
|
||||
}
|
||||
|
||||
sub name_selection {
|
||||
return 'Aldimor del Bosque del Héroe.';
|
||||
}
|
||||
|
||||
sub description {
|
||||
return 'La raza de la naturaleza y la magia.';
|
||||
}
|
||||
|
||||
sub is_playable {
|
||||
return 1;
|
||||
}
|
||||
1;
|
77
lib/LasTres/Races.pm
Normal file
77
lib/LasTres/Races.pm
Normal file
@ -0,0 +1,77 @@
|
||||
package LasTres::Races;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use feature 'signatures';
|
||||
|
||||
use Moo;
|
||||
use Module::Pluggable search_path => ['LasTres::Race'];
|
||||
|
||||
has hash => (
|
||||
is => 'lazy',
|
||||
);
|
||||
|
||||
has hash_playable => (
|
||||
is => 'lazy',
|
||||
)
|
||||
|
||||
has hash_all => (
|
||||
is => 'lazy',
|
||||
)
|
||||
|
||||
sub _build_hash_all($self) {
|
||||
return { map { $_ => $self->hash->{$_}->hash } (keys %{$self->hash}) };
|
||||
}
|
||||
|
||||
sub _build_hash_all_playable($self) {
|
||||
my %hash = %{$self->hash};
|
||||
for my $race_key (keys %hash) {
|
||||
my $race = $hash{$race_key};
|
||||
if (!$race->is_playable) {
|
||||
delete $hash{$race_key};
|
||||
}
|
||||
$hash{$race_key} = $race->hash;
|
||||
}
|
||||
return \%hash;
|
||||
}
|
||||
|
||||
sub _build_hash {
|
||||
my $self = shift;
|
||||
my $hash = {};
|
||||
my @races = $self->plugins();
|
||||
for my $race (@races) {
|
||||
$hash{$race->identifier} = $race;
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
sub _build_hash_playable {
|
||||
my $self = shift;
|
||||
my $hash = {@{$self->hash}};
|
||||
for my $identifier_race (keys %$hash) {
|
||||
my $race = $hash->{$identifier_race};
|
||||
if (!$race->is_playable) {
|
||||
delete $hash->{$identifier_race};
|
||||
}
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
sub get($self, $race_identifier) {
|
||||
return $self->hash->{$race_identifier};
|
||||
}
|
||||
|
||||
sub get_playable($self, $race_identifier) {
|
||||
my $race = $self->hash->{$race_identifier};
|
||||
if (!defined $race) {
|
||||
return undef;
|
||||
}
|
||||
if (!$race->is_playable) {
|
||||
return undef;
|
||||
}
|
||||
return $race;
|
||||
}
|
||||
1;
|
40
lib/LasTres/Schema.pm
Normal file
40
lib/LasTres/Schema.pm
Normal file
@ -0,0 +1,40 @@
|
||||
package LasTres::Schema;
|
||||
our $VERSION = 1;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use feature 'signatures';
|
||||
|
||||
use LasTres;
|
||||
|
||||
use parent 'DBIx::Class::Schema';
|
||||
|
||||
__PACKAGE__->load_namespaces();
|
||||
|
||||
sub Schema($class) {
|
||||
my $app = LasTres->new;
|
||||
my $config = $app->{config};
|
||||
my $database_config = $config->{database};
|
||||
my $dbname = $database_config->{dbname};
|
||||
my $host = $database_config->{host};
|
||||
my $port = $database_config->{port};
|
||||
my $user = $database_config->{user};
|
||||
my $password = $database_config->{password};
|
||||
my $dsn = 'dbi:Pg:';
|
||||
if (!defined $dbname) {
|
||||
die "The key database/dbname must be configured.";
|
||||
}
|
||||
$dsn .= "dbname=$dbname";
|
||||
if (defined $host) {
|
||||
$dsn .= ";host=$host";
|
||||
}
|
||||
if (defined $port) {
|
||||
$dsn .= ";port=$port";
|
||||
}
|
||||
# Undef is perfectly fine for username and password.
|
||||
return $class->connect($dsn, $user, $password);
|
||||
}
|
||||
1;
|
79
lib/LasTres/Schema/Result/CompanionNPC.pm
Normal file
79
lib/LasTres/Schema/Result/CompanionNPC.pm
Normal file
@ -0,0 +1,79 @@
|
||||
package LasTres::Schema::Result::CompanionNPC;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table('player_companion_npcs');
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
uuid => {
|
||||
data_type => 'uuid',
|
||||
default_value => \'uuid_generate_v4()',
|
||||
is_nullable => 0,
|
||||
},
|
||||
owner => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
identifier => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
nick => {
|
||||
data_type => 'text',
|
||||
is_nullable => 1,
|
||||
},
|
||||
race => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
level => {
|
||||
data_type => 'integer',
|
||||
default_value => \'1',
|
||||
is_nullable => 0,
|
||||
},
|
||||
exp => {
|
||||
data_type => 'integer',
|
||||
default_value => \'1',
|
||||
is_nullable => 0,
|
||||
},
|
||||
equipment => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
stats => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
skills => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
spells => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
inventory => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
}
|
||||
|
||||
);
|
||||
__PACKAGE__->set_primary_key('uuid');
|
||||
|
||||
__PACKAGE__->belongs_to('stats', 'LasTres::Schema::Result::Stats');
|
||||
__PACKAGE__->belongs_to('inventory', 'LasTres::Schema::Result::Inventory');
|
||||
__PACKAGE__->belongs_to('equipment', 'LasTres::Schema::Result::Equipment');
|
||||
__PACKAGE__->belongs_to('skills', 'LasTres::Schema::Result::SkillLikeList');
|
||||
__PACKAGE__->belongs_to('spells', 'LasTres::Schema::Result::SkillLikeList');
|
||||
__PACKAGE__->belongs_to('owner', 'LasTres::Schema::Result::PJ');
|
||||
1;
|
21
lib/LasTres/Schema/Result/Equipment.pm
Normal file
21
lib/LasTres/Schema/Result/Equipment.pm
Normal file
@ -0,0 +1,21 @@
|
||||
package LasTres::Schema::Result::Equipment;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table('equipment');
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
uuid => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
},
|
||||
);
|
||||
|
||||
__PACKAGE__->set_primary_key('uuid');
|
||||
__PACKAGE__->has_many('items', 'LasTres::Schema::Result::EquipmentItem', 'equipment');
|
||||
1;
|
34
lib/LasTres/Schema/Result/EquipmentItem.pm
Normal file
34
lib/LasTres/Schema/Result/EquipmentItem.pm
Normal file
@ -0,0 +1,34 @@
|
||||
package LasTres::Schema::Result::EquipmentItem;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table('equipment_items');
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
kind => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
equipment => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
identifier => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
quantity => {
|
||||
data_type => 'Integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
);
|
||||
|
||||
__PACKAGE__->set_primary_key('kind', 'equipment');
|
||||
__PACKAGE__->belongs_to('equipment', 'LasTres::Schema::Result::Equipment');
|
||||
1;
|
20
lib/LasTres/Schema/Result/Inventory.pm
Normal file
20
lib/LasTres/Schema/Result/Inventory.pm
Normal file
@ -0,0 +1,20 @@
|
||||
package LasTres::Schema::Result::Inventory;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table('inventories');
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
uuid => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
},
|
||||
);
|
||||
__PACKAGE__->set_primary_key('uuid');
|
||||
__PACKAGE__->has_many('items', 'LasTres::Schema::Result::InventoryItem', 'inventory');
|
||||
1;
|
35
lib/LasTres/Schema/Result/InventoryItem.pm
Normal file
35
lib/LasTres/Schema/Result/InventoryItem.pm
Normal file
@ -0,0 +1,35 @@
|
||||
package LasTres::Schema::Result::InventoryItem;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table('inventory_items');
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
uuid => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
default_value => \'uuid_generate_v4()',
|
||||
},
|
||||
inventory => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
identifier => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
quantity => {
|
||||
data_type => 'Integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
);
|
||||
|
||||
__PACKAGE__->set_primary_key('uuid');
|
||||
__PACKAGE__->belongs_to('inventory', 'LasTres::Schema::Result::Inventory');
|
||||
1;
|
99
lib/LasTres/Schema/Result/PJ.pm
Normal file
99
lib/LasTres/Schema/Result/PJ.pm
Normal file
@ -0,0 +1,99 @@
|
||||
package LasTres::Schema::Result::PJ;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table('player_pjs');
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
uuid => {
|
||||
data_type => 'uuid',
|
||||
default_value => \'uuid_generate_v4()',
|
||||
is_nullable => 0,
|
||||
},
|
||||
owner => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
full_name => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
short_name => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
nick => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
race => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
team => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
},
|
||||
creation_date => {
|
||||
data_type => 'timestamp',
|
||||
default_value => \'NOW()',
|
||||
is_nullable => 0,
|
||||
},
|
||||
last_activity => {
|
||||
data_type => 'timestamp',
|
||||
default_value => \'NOW()',
|
||||
is_nullable => 0,
|
||||
},
|
||||
level => {
|
||||
data_type => 'integer',
|
||||
default_value => \'1',
|
||||
is_nullable => 0,
|
||||
},
|
||||
exp => {
|
||||
data_type => 'integer',
|
||||
default_value => \'1',
|
||||
is_nullable => 0,
|
||||
},
|
||||
equipment => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
stats => {
|
||||
data_type => 'uuid',
|
||||
is_foreign_key => 1,
|
||||
is_nullable => 0,
|
||||
},
|
||||
skills => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
spells => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
inventory => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
}
|
||||
|
||||
);
|
||||
__PACKAGE__->set_primary_key('uuid');
|
||||
|
||||
__PACKAGE__->has_many('npcs', 'LasTres::Schema::Result::CompanionNPC', 'owner');
|
||||
__PACKAGE__->belongs_to('stats', 'LasTres::Schema::Result::Stats');
|
||||
__PACKAGE__->belongs_to('inventory', 'LasTres::Schema::Result::Inventory');
|
||||
__PACKAGE__->belongs_to('skills', 'LasTres::Schema::Result::SkillLikeList');
|
||||
__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');
|
||||
1;
|
52
lib/LasTres/Schema/Result/Player.pm
Normal file
52
lib/LasTres/Schema/Result/Player.pm
Normal file
@ -0,0 +1,52 @@
|
||||
package LasTres::Schema::Result::Player;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table('players');
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
uuid => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
},
|
||||
username => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
encrypted_password => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
email => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
verified => {
|
||||
data_type => 'boolean',
|
||||
is_nullable => 0,
|
||||
},
|
||||
verification_token => {
|
||||
data_type => 'text',
|
||||
is_nullable => 1,
|
||||
},
|
||||
register_date => {
|
||||
data_type => 'timestamp',
|
||||
is_nullable => 0,
|
||||
default_value => \'NOW()',
|
||||
},
|
||||
last_activity => {
|
||||
data_type => 'timestamp',
|
||||
is_nullable => 0,
|
||||
default_value => \'NOW()',
|
||||
},
|
||||
);
|
||||
__PACKAGE__->set_primary_key('uuid');
|
||||
__PACKAGE__->has_many('pjs', 'LasTres::Schema::Result::PJ', 'owner');
|
||||
__PACKAGE__->add_unique_constraint("unique_constraint_username", ['username']);
|
||||
__PACKAGE__->add_unique_constraint("unique_constraint_email", ["email"]);
|
||||
1;
|
29
lib/LasTres/Schema/Result/SkillLikeItem.pm
Normal file
29
lib/LasTres/Schema/Result/SkillLikeItem.pm
Normal file
@ -0,0 +1,29 @@
|
||||
package LasTres::Schema::Result::SkillLikeItem;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table('skill_like_items');
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
identifier => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
owner_list => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
},
|
||||
level => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
default_value => \'1',
|
||||
}
|
||||
);
|
||||
__PACKAGE__->set_primary_key('identifier', 'owner_list');
|
||||
__PACKAGE__->belongs_to('owner_list', 'LasTres::Schema::Result::SkillLikeList');
|
||||
1;
|
21
lib/LasTres/Schema/Result/SkillLikeList.pm
Normal file
21
lib/LasTres/Schema/Result/SkillLikeList.pm
Normal file
@ -0,0 +1,21 @@
|
||||
package LasTres::Schema::Result::SkillLikeList;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table('skill_like_lists');
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
uuid => {
|
||||
data_type => 'uuid',
|
||||
default_value => \'uuid_generate_v4()',
|
||||
is_nullable => 0,
|
||||
},
|
||||
);
|
||||
__PACKAGE__->set_primary_key('uuid');
|
||||
__PACKAGE__->has_many('items', 'LasTres::Schema::Result::SkillLikeItem', 'owner_list');
|
||||
1;
|
51
lib/LasTres/Schema/Result/Stats.pm
Normal file
51
lib/LasTres/Schema/Result/Stats.pm
Normal file
@ -0,0 +1,51 @@
|
||||
package LasTres::Schema::Result::Stats;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table('stats');
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
uuid => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
},
|
||||
health => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
mana => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
strength => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
resistance => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
magic => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
speed => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
intelligence => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
charisma => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
);
|
||||
__PACKAGE__->set_primary_key('uuid');
|
||||
1;
|
49
lib/LasTres/Schema/Result/Team.pm
Normal file
49
lib/LasTres/Schema/Result/Team.pm
Normal file
@ -0,0 +1,49 @@
|
||||
package LasTres::Schema::Result::Team;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table('teams');
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
uuid => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
},
|
||||
leader => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
is_foreign_key => 1,
|
||||
},
|
||||
name => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
planet => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
super_area => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
area => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
location => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
__PACKAGE__->set_primary_key('uuid');
|
||||
__PACKAGE__->add_unique_constraint(u_name => ['name']);
|
||||
__PACKAGE__->has_many('members', 'LasTres::Schema::Result::PJ', 'team');
|
||||
__PACKAGE__->belongs_to('leader', 'LasTres::Schema::Result::PJ');
|
||||
1;
|
11
lib/LasTres/SuperArea.pm
Normal file
11
lib/LasTres/SuperArea.pm
Normal file
@ -0,0 +1,11 @@
|
||||
package LasTres::SuperArea;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Moo::Role;
|
||||
|
||||
requires qw/identifier areas name description parent/;
|
||||
1;
|
39
package.json
Normal file
39
package.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "LasTres",
|
||||
"version": "0.1.1",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.0",
|
||||
"babel-loader": "^9.1.2",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-config-standard-with-typescript": "^34.0.1",
|
||||
"eslint-import-resolver-typescript": "^3.5.3",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-n": "^15.6.1",
|
||||
"eslint-plugin-no-relative-import-paths": "^1.5.2",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^5.0.2",
|
||||
"typescript-transform-paths": "^3.4.6",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.38.1",
|
||||
"webpack-cli": "^4.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
}
|
103
public/css/styles.css
Normal file
103
public/css/styles.css
Normal file
@ -0,0 +1,103 @@
|
||||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
min-height: 100%; }
|
||||
body div.width-max-content {
|
||||
width: max-content; }
|
||||
body div#game-container {
|
||||
min-height: 100%; }
|
||||
body div#game-container nav.menu-bar {
|
||||
width: 100%;
|
||||
background: grey;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center; }
|
||||
body div#game-container nav.menu-bar a {
|
||||
font-size: 1.7rem;
|
||||
display: inline-block;
|
||||
background: chocolate;
|
||||
border-radius: 0.3rem;
|
||||
color: ghostwhite;
|
||||
text-decoration: none;
|
||||
margin-right: 0.7rem;
|
||||
margin-left: 0.7rem;
|
||||
height: 100%; }
|
||||
body div#game-container nav.menu-bar a:last-child {
|
||||
margin-right: 0px; }
|
||||
body div#game-container nav.menu-bar a:hover {
|
||||
background: ghostwhite;
|
||||
color: black; }
|
||||
body div.pj-selection-menu {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100%;
|
||||
background: ghostwhite;
|
||||
flex-direction: column; }
|
||||
body div.pj-selection-menu div.pj-selection-menu-container {
|
||||
background: ghostwhite;
|
||||
color: black;
|
||||
padding: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 80%;
|
||||
min-height: 100%;
|
||||
flex-direction: column;
|
||||
overflow: scroll; }
|
||||
body div.pj-selection-menu div.pj-selection-menu-container div.pj-list {
|
||||
background: ghostwhite;
|
||||
width: 90%;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-grow: 1px;
|
||||
gap: 8px; }
|
||||
body div.pj-selection-menu div.pj-selection-menu-container div.pj-list a {
|
||||
width: 10em;
|
||||
height: 10em;
|
||||
background: white;
|
||||
border: solid 1px black;
|
||||
box-shadow: 3px 1px 3px 3px #333;
|
||||
color: blue;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
text-decoration: none; }
|
||||
body div.pj-selection-menu div.pj-selection-menu-container div.pj-list a:hover {
|
||||
color: yellow;
|
||||
background: gray; }
|
||||
body div.login-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background: url("/img/wallpaper.jpg") no-repeat center black;
|
||||
flex-direction: column; }
|
||||
body div.login-container div.login-contained {
|
||||
background: #001e8b;
|
||||
color: azure;
|
||||
padding: 30px;
|
||||
border: solid 1px black;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column; }
|
||||
body div.login-container div.login-contained input {
|
||||
display: block; }
|
||||
body div.login-container div.login-contained button {
|
||||
display: block; }
|
||||
body div.login-container div.login-contained div.login-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: max-content; }
|
||||
body div.login-container div.login-contained div.login-form div.align-self-end {
|
||||
align-self: end; }
|
||||
body div.login-container div.login-contained a {
|
||||
color: aquamarine; }
|
||||
body div.login-container div.login-contained a:hover {
|
||||
color: yellow; }
|
125
public/css/styles.scss
Normal file
125
public/css/styles.scss
Normal file
@ -0,0 +1,125 @@
|
||||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
min-height: 100%;
|
||||
div.width-max-content {
|
||||
width: max-content;
|
||||
}
|
||||
div#game-container {
|
||||
min-height: 100%;
|
||||
nav.menu-bar {
|
||||
width: 100%;
|
||||
background: grey;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
a {
|
||||
font-size: 1.7rem;
|
||||
display: inline-block;
|
||||
background: chocolate;
|
||||
border-radius: 0.3rem;
|
||||
color: ghostwhite;
|
||||
text-decoration: none;
|
||||
margin-right: 0.7rem;
|
||||
margin-left: 0.7rem;
|
||||
height: 100%;
|
||||
&:last-child {
|
||||
margin-right: 0px;
|
||||
}
|
||||
&:hover {
|
||||
background: ghostwhite;
|
||||
color: black;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div.pj-selection-menu {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100%;
|
||||
background: ghostwhite;
|
||||
flex-direction: column;
|
||||
div.pj-selection-menu-container {
|
||||
background: ghostwhite;
|
||||
color: black;
|
||||
padding: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 80%;
|
||||
min-height: 100%;
|
||||
flex-direction: column;
|
||||
overflow: scroll;
|
||||
div.pj-list {
|
||||
background: ghostwhite;
|
||||
width: 90%;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-grow: 1px;
|
||||
gap: 8px;
|
||||
|
||||
a {
|
||||
width: 10em;
|
||||
height: 10em;
|
||||
background: white;
|
||||
border: solid 1px black;
|
||||
box-shadow: 3px 1px 3px 3px #333;
|
||||
color: blue;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
&:hover {
|
||||
color: yellow;
|
||||
background: gray;
|
||||
}
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div.login-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background: url('/img/wallpaper.jpg') no-repeat center black;
|
||||
flex-direction: column;
|
||||
div.login-contained {
|
||||
background: rgba(0, 30, 139, 1);
|
||||
color: azure;
|
||||
padding: 30px;
|
||||
border: solid 1px black;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
input {
|
||||
display: block;
|
||||
}
|
||||
button {
|
||||
display: block;
|
||||
}
|
||||
div.login-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: max-content;
|
||||
div.align-self-end {
|
||||
align-self: end;
|
||||
}
|
||||
}
|
||||
a {
|
||||
color: aquamarine;
|
||||
&:hover {
|
||||
color: yellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
public/img/wallpaper.jpg
Normal file
BIN
public/img/wallpaper.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
270
public/js/bundle.js
Normal file
270
public/js/bundle.js
Normal file
File diff suppressed because one or more lines are too long
29
script/install.pl
Normal file
29
script/install.pl
Normal file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use aliased 'DBIx::Class::DeploymentHandler' => 'DH';
|
||||
use Getopt::Long;
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use LasTres::Schema;
|
||||
|
||||
my $force_overwrite = 0;
|
||||
|
||||
unless ( GetOptions( 'force_overwrite!' => \$force_overwrite ) ) {
|
||||
die "Invalid options";
|
||||
}
|
||||
|
||||
my $schema = LasTres::Schema->Schema;
|
||||
|
||||
my $dh = DH->new(
|
||||
{
|
||||
schema => $schema,
|
||||
script_directory => "$FindBin::Bin/../dbicdh",
|
||||
databases => 'PostgreSQL',
|
||||
sql_translator_args => { add_drop_table => 0 },
|
||||
force_overwrite => $force_overwrite,
|
||||
}
|
||||
);
|
||||
|
||||
$dh->install;
|
11
script/las_tres
Executable file
11
script/las_tres
Executable file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Mojo::File qw(curfile);
|
||||
use lib curfile->dirname->sibling('lib')->to_string;
|
||||
use Mojolicious::Commands;
|
||||
|
||||
# Start command line interface for application
|
||||
Mojolicious::Commands->start_app('LasTres');
|
35
script/prepare.pl
Normal file
35
script/prepare.pl
Normal file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use aliased 'DBIx::Class::DeploymentHandler' => 'DH';
|
||||
use Getopt::Long;
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use LasTres::Schema;
|
||||
|
||||
my $force_overwrite = 1;
|
||||
|
||||
unless ( GetOptions( 'force_overwrite!' => \$force_overwrite ) ) {
|
||||
die "Invalid options";
|
||||
}
|
||||
|
||||
my $schema = LasTres::Schema->Schema;
|
||||
|
||||
my $dh = DH->new(
|
||||
{
|
||||
schema => $schema,
|
||||
script_directory => "$FindBin::Bin/../dbicdh",
|
||||
databases => 'PostgreSQL',
|
||||
sql_translator_args => { add_drop_table => 0 },
|
||||
force_overwrite => $force_overwrite,
|
||||
}
|
||||
);
|
||||
|
||||
$dh->prepare_deploy;
|
||||
eval {
|
||||
$dh->prepare_upgrade;
|
||||
};
|
||||
if ($@) {
|
||||
$dh->prepare_install;
|
||||
}
|
29
script/upgrade.pl
Normal file
29
script/upgrade.pl
Normal file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use aliased 'DBIx::Class::DeploymentHandler' => 'DH';
|
||||
use Getopt::Long;
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use LasTres::Schema;
|
||||
|
||||
my $force_overwrite = 1;
|
||||
|
||||
unless ( GetOptions( 'force_overwrite!' => \$force_overwrite ) ) {
|
||||
die "Invalid options";
|
||||
}
|
||||
|
||||
my $schema = LasTres::Schema->Schema;
|
||||
|
||||
my $dh = DH->new(
|
||||
{
|
||||
schema => $schema,
|
||||
script_directory => "$FindBin::Bin/../dbicdh",
|
||||
databases => 'PostgreSQL',
|
||||
sql_translator_args => { add_drop_table => 0 },
|
||||
force_overwrite => $force_overwrite,
|
||||
}
|
||||
);
|
||||
|
||||
$dh->upgrade;
|
20
templates/root/index.html.ep
Normal file
20
templates/root/index.html.ep
Normal file
@ -0,0 +1,20 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/css/styles.css"/>
|
||||
<meta name="viewport" content="height = device-height,
|
||||
width = device-width,
|
||||
initial-scale = 1.0,
|
||||
minimum-scale = 1.0,
|
||||
maximum-scale = 1.0,
|
||||
user-scalable = no,
|
||||
target-densitydpi = device-dpi"/>
|
||||
</head>
|
||||
<body>
|
||||
<noscript style="font-size: 40px">
|
||||
<p>LasTres cannot work without javascript, consider using the API to make you own client if this is a concern to you.</p>
|
||||
</noscript>
|
||||
<div id="game-container">
|
||||
</div>
|
||||
<script src="/js/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
30
tsconfig.json
Normal file
30
tsconfig.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./public/js/",
|
||||
"noImplicitAny": true,
|
||||
"module": "es2020",
|
||||
"target": "es2020",
|
||||
"jsx": "react",
|
||||
"allowJs": true,
|
||||
"moduleResolution": "node",
|
||||
"strictNullChecks": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@lastres/*": ["js-src/*"],
|
||||
"/*": ["js-src/*"]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"transform": "typescript-transform-paths"
|
||||
},
|
||||
{
|
||||
"transform": "typescript-transform-paths",
|
||||
"afterDeclarations": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"js-src/*.ts", "js-src/*/*.ts",
|
||||
"js-src/*.tsx", "js-src/*/*.tsx"
|
||||
]
|
||||
}
|
38
webpack.config.js
Normal file
38
webpack.config.js
Normal file
@ -0,0 +1,38 @@
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
entry: './js-src/index.tsx',
|
||||
mode: 'development',
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
path: path.resolve(__dirname, 'public/js/')
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
roots: [
|
||||
path.resolve(__dirname, 'js-src/')
|
||||
],
|
||||
alias: {
|
||||
'@lastres': path.resolve(__dirname, 'js-src')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.jpe?g|png$/,
|
||||
exclude: /node_modules/,
|
||||
use: ['url-loader', 'file-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user