Move webclient to cardgage
This commit is contained in:
parent
9feb703f01
commit
14ac80df84
27 changed files with 2 additions and 9550 deletions
|
@ -2,6 +2,8 @@
|
|||
|
||||
Tools and services needed for the MCG backend to work.
|
||||
|
||||
## Inside this repository
|
||||
|
||||
### draftbot
|
||||
|
||||
Bot for drafting MLP:CCG sets and cubes on MCG
|
||||
|
@ -9,7 +11,3 @@ Bot for drafting MLP:CCG sets and cubes on MCG
|
|||
### buildmap / convertsets / genpics
|
||||
|
||||
Tools for converting data/image packs from OCTGN to MCG/Cardgage
|
||||
|
||||
### webclient
|
||||
|
||||
Client for connecting to a Cardgage server and creating/joining test rooms
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
> 1%
|
||||
last 2 versions
|
|
@ -1,14 +0,0 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
extends: ["plugin:vue/essential", "@vue/prettier", "@vue/typescript"],
|
||||
rules: {
|
||||
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
|
||||
},
|
||||
parserOptions: {
|
||||
parser: "@typescript-eslint/parser"
|
||||
}
|
||||
};
|
21
webclient/.gitignore
vendored
21
webclient/.gitignore
vendored
|
@ -1,21 +0,0 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
|
@ -1,3 +0,0 @@
|
|||
# webclient
|
||||
|
||||
Web-client for browsing, creating and joining rooms on Cardgage servers
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
presets: ["@vue/app"]
|
||||
};
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"name": "webclient",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"buefy": "^0.7.4",
|
||||
"core-js": "^3.1.2",
|
||||
"vue": "^2.6.10",
|
||||
"vue-class-component": "^7.0.2",
|
||||
"vue-property-decorator": "^8.1.0",
|
||||
"vuex": "^3.0.1",
|
||||
"vuex-class": "^0.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.0.0-alpha.1",
|
||||
"@vue/cli-plugin-eslint": "^4.0.0-alpha.1",
|
||||
"@vue/cli-plugin-typescript": "^4.0.0-alpha.1",
|
||||
"@vue/cli-service": "^4.0.0-alpha.1",
|
||||
"@vue/eslint-config-prettier": "^4.0.1",
|
||||
"@vue/eslint-config-typescript": "^4.0.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"node-sass": "^4.9.0",
|
||||
"sass": "^1.19.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"typescript": "^3.4.5",
|
||||
"vue-cli-plugin-buefy": "^0.3.6",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {}
|
||||
}
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
// prettier.config.js or .prettierrc.js
|
||||
module.exports = {
|
||||
tabWidth: 4,
|
||||
useTabs: true
|
||||
};
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
|
@ -1,24 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="//cdn.materialdesignicons.com/2.0.46/css/materialdesignicons.min.css"
|
||||
/>
|
||||
<title>Cardgage Web Client</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong
|
||||
>We're sorry but webclient doesn't work properly without
|
||||
JavaScript enabled. Please enable it to continue.</strong
|
||||
>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
|
@ -1,77 +0,0 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<section class="hero is-primary">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<h1 class="title">{{ title }}</h1>
|
||||
<h2 class="subtitle">{{ subtitle }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="container">
|
||||
<ConnectForm v-if="!isConnected" />
|
||||
<RoomList v-if="inLobby" />
|
||||
<RoomLog v-if="inRoom" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.hero {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-property-decorator";
|
||||
import { State } from "vuex-class";
|
||||
import ConnectForm from "@/components/ConnectForm.vue";
|
||||
import RoomList from "@/components/RoomList.vue";
|
||||
import RoomLog from "@/components/RoomLog.vue";
|
||||
import { ServerState } from "./store/server";
|
||||
import { RoomState } from "./store/room";
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
ConnectForm,
|
||||
RoomList,
|
||||
RoomLog
|
||||
}
|
||||
})
|
||||
export default class App extends Vue {
|
||||
@State("server") private server!: ServerState;
|
||||
@State("room") private room!: RoomState;
|
||||
|
||||
private data() {
|
||||
return {};
|
||||
}
|
||||
|
||||
private get isConnected(): boolean {
|
||||
return this.server.connected;
|
||||
}
|
||||
|
||||
private get inLobby(): boolean {
|
||||
return this.isConnected && !this.room.in_room;
|
||||
}
|
||||
|
||||
private get inRoom(): boolean {
|
||||
return this.isConnected && this.room.in_room;
|
||||
}
|
||||
|
||||
private get title(): string {
|
||||
return "Cardgage test client";
|
||||
}
|
||||
|
||||
private get subtitle(): string {
|
||||
if (this.inRoom && this.room.room) {
|
||||
return `Inside room "${this.room.room.name}" (${
|
||||
this.room.room.id
|
||||
})`;
|
||||
}
|
||||
if (this.isConnected) {
|
||||
return "Browsing " + this.server.server;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,153 +0,0 @@
|
|||
// Included below are all the defined variables from Bulma
|
||||
// Modify as needed, removing the !default attribute.
|
||||
|
||||
// Colors
|
||||
|
||||
$black: hsl(0, 0%, 4%) !default;
|
||||
$black-bis: hsl(0, 0%, 7%) !default;
|
||||
$black-ter: hsl(0, 0%, 14%) !default;
|
||||
|
||||
$grey-darker: hsl(0, 0%, 21%) !default;
|
||||
$grey-dark: hsl(0, 0%, 29%) !default;
|
||||
$grey: hsl(0, 0%, 48%) !default;
|
||||
$grey-light: hsl(0, 0%, 71%) !default;
|
||||
$grey-lighter: hsl(0, 0%, 86%) !default;
|
||||
|
||||
$white-ter: hsl(0, 0%, 96%) !default;
|
||||
$white-bis: hsl(0, 0%, 98%) !default;
|
||||
$white: hsl(0, 0%, 100%) !default;
|
||||
|
||||
$orange: hsl(14, 100%, 53%) !default;
|
||||
$yellow: hsl(48, 100%, 67%) !default;
|
||||
$green: hsl(141, 71%, 48%) !default;
|
||||
$turquoise: hsl(171, 100%, 41%) !default;
|
||||
$cyan: hsl(204, 86%, 53%) !default;
|
||||
$blue: hsl(217, 71%, 53%) !default;
|
||||
$purple: hsl(271, 100%, 71%) !default;
|
||||
$red: hsl(348, 100%, 61%) !default;
|
||||
|
||||
// Typography
|
||||
|
||||
$family-sans-serif: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
"Helvetica", "Arial", sans-serif !default;
|
||||
$family-monospace: monospace !default;
|
||||
$render-mode: optimizeLegibility !default;
|
||||
|
||||
$size-1: 3rem !default;
|
||||
$size-2: 2.5rem !default;
|
||||
$size-3: 2rem !default;
|
||||
$size-4: 1.5rem !default;
|
||||
$size-5: 1.25rem !default;
|
||||
$size-6: 1rem !default;
|
||||
$size-7: 0.75rem !default;
|
||||
|
||||
$weight-light: 300 !default;
|
||||
$weight-normal: 400 !default;
|
||||
$weight-medium: 500 !default;
|
||||
$weight-semibold: 600 !default;
|
||||
$weight-bold: 700 !default;
|
||||
|
||||
// Responsiveness
|
||||
|
||||
// The container horizontal gap, which acts as the offset for breakpoints
|
||||
$gap: 32px !default;
|
||||
// 960, 1152, and 1344 have been chosen because they are divisible by both 12 and 16
|
||||
$tablet: 769px !default;
|
||||
// 960px container + 4rem
|
||||
$desktop: 960px + (2 * $gap) !default;
|
||||
// 1152px container + 4rem
|
||||
$widescreen: 1152px + (2 * $gap) !default;
|
||||
// 1344px container + 4rem;
|
||||
$fullhd: 1344px + (2 * $gap) !default;
|
||||
|
||||
// Miscellaneous
|
||||
|
||||
$easing: ease-out !default;
|
||||
$radius-small: 2px !default;
|
||||
$radius: 3px !default;
|
||||
$radius-large: 5px !default;
|
||||
$radius-rounded: 290486px !default;
|
||||
$speed: 86ms !default;
|
||||
|
||||
// Flags
|
||||
|
||||
$variable-columns: true !default;
|
||||
|
||||
// The default Bulma derived variables are declared below
|
||||
|
||||
$primary: $turquoise !default;
|
||||
|
||||
$info: $cyan !default;
|
||||
$success: $green !default;
|
||||
$warning: $yellow !default;
|
||||
$danger: $red !default;
|
||||
|
||||
$light: $white-ter !default;
|
||||
$dark: $grey-darker !default;
|
||||
|
||||
// Invert colors
|
||||
|
||||
$orange-invert: findColorInvert($orange) !default;
|
||||
$yellow-invert: findColorInvert($yellow) !default;
|
||||
$green-invert: findColorInvert($green) !default;
|
||||
$turquoise-invert: findColorInvert($turquoise) !default;
|
||||
$cyan-invert: findColorInvert($cyan) !default;
|
||||
$blue-invert: findColorInvert($blue) !default;
|
||||
$purple-invert: findColorInvert($purple) !default;
|
||||
$red-invert: findColorInvert($red) !default;
|
||||
|
||||
$primary-invert: $turquoise-invert !default;
|
||||
$info-invert: $cyan-invert !default;
|
||||
$success-invert: $green-invert !default;
|
||||
$warning-invert: $yellow-invert !default;
|
||||
$danger-invert: $red-invert !default;
|
||||
$light-invert: $dark !default;
|
||||
$dark-invert: $light !default;
|
||||
|
||||
// General colors
|
||||
|
||||
$background: $white-ter !default;
|
||||
|
||||
$border: $grey-lighter !default;
|
||||
$border-hover: $grey-light !default;
|
||||
|
||||
// Text colors
|
||||
|
||||
$text: $grey-dark !default;
|
||||
$text-invert: findColorInvert($text) !default;
|
||||
$text-light: $grey !default;
|
||||
$text-strong: $grey-darker !default;
|
||||
|
||||
// Code colors
|
||||
|
||||
$code: $red !default;
|
||||
$code-background: $background !default;
|
||||
|
||||
$pre: $text !default;
|
||||
$pre-background: $background !default;
|
||||
|
||||
// Link colors
|
||||
|
||||
$link: $blue !default;
|
||||
$link-invert: $blue-invert !default;
|
||||
$link-visited: $purple !default;
|
||||
|
||||
$link-hover: $grey-darker !default;
|
||||
$link-hover-border: $grey-light !default;
|
||||
|
||||
$link-focus: $grey-darker !default;
|
||||
$link-focus-border: $blue !default;
|
||||
|
||||
$link-active: $grey-darker !default;
|
||||
$link-active-border: $grey-dark !default;
|
||||
|
||||
// Typography
|
||||
|
||||
$family-primary: $family-sans-serif !default;
|
||||
$family-code: $family-monospace !default;
|
||||
|
||||
$size-small: $size-7 !default;
|
||||
$size-normal: $size-6 !default;
|
||||
$size-medium: $size-5 !default;
|
||||
$size-large: $size-4 !default;
|
|
@ -1,10 +0,0 @@
|
|||
@import "~bulma/sass/utilities/initial-variables";
|
||||
@import "~bulma/sass/utilities/functions";
|
||||
@import "variables";
|
||||
|
||||
@import "~bulma/sass/utilities/derived-variables";
|
||||
|
||||
@import "~bulma";
|
||||
@import "~buefy/src/scss/buefy";
|
||||
|
||||
@import "overrides";
|
|
@ -1,48 +0,0 @@
|
|||
<template>
|
||||
<section>
|
||||
<form @submit.prevent="tryConnect">
|
||||
<b-notification
|
||||
type="is-danger"
|
||||
aria-close-label="Close notification"
|
||||
role="alert"
|
||||
v-if="server.connectionError"
|
||||
>
|
||||
{{ server.connectionError }}
|
||||
</b-notification>
|
||||
<b-field label="Server address">
|
||||
<b-input size="is-large" v-model="addr"></b-input>
|
||||
</b-field>
|
||||
<b-field>
|
||||
<button type="submit" class="button is-primary">Connect</button>
|
||||
</b-field>
|
||||
<b-loading
|
||||
is-full-page
|
||||
:active.sync="server.connecting"
|
||||
></b-loading>
|
||||
</form>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Action, State } from "vuex-class";
|
||||
import { Component, Vue } from "vue-property-decorator";
|
||||
import { ServerState } from "@/store/server";
|
||||
|
||||
@Component({})
|
||||
export default class ConnectForm extends Vue {
|
||||
@State("server") private server!: ServerState;
|
||||
@Action("connect", { namespace: "server" }) private connect: any;
|
||||
private addr!: string;
|
||||
private loading!: boolean;
|
||||
|
||||
private data() {
|
||||
return {
|
||||
addr: "http://192.168.22.22"
|
||||
};
|
||||
}
|
||||
|
||||
private async tryConnect() {
|
||||
this.connect(this.addr);
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,107 +0,0 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<b-button @click="create" icon-left="library-plus" type="is-primary"
|
||||
>Create</b-button
|
||||
>
|
||||
<b-table
|
||||
striped
|
||||
hoverable
|
||||
:data="rooms"
|
||||
:columns="columns"
|
||||
@click="rowClicked"
|
||||
></b-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.b-table {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-property-decorator";
|
||||
import { State, Action } from "vuex-class";
|
||||
import { ServerState } from "@/store/server";
|
||||
import { Room } from "@/store/room";
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: "game_id",
|
||||
label: "Game"
|
||||
},
|
||||
{
|
||||
field: "name",
|
||||
label: "Name"
|
||||
},
|
||||
{
|
||||
field: "creator",
|
||||
label: "Creator"
|
||||
},
|
||||
{
|
||||
field: "can_spectate",
|
||||
label: "Spectators allowed"
|
||||
},
|
||||
{
|
||||
field: "players",
|
||||
label: "Players",
|
||||
numeric: true
|
||||
},
|
||||
{
|
||||
field: "spectators",
|
||||
label: "Spectators",
|
||||
numeric: true
|
||||
}
|
||||
];
|
||||
|
||||
interface RoomRow {
|
||||
id: string;
|
||||
game_id: string;
|
||||
name: string;
|
||||
creator: string;
|
||||
players: number;
|
||||
spectators: number;
|
||||
can_spectate: string;
|
||||
}
|
||||
|
||||
@Component({})
|
||||
export default class RoomList extends Vue {
|
||||
@State("server") private server!: ServerState;
|
||||
@Action("joinRoom", { namespace: "server" }) private join: any;
|
||||
@Action("createRoom", { namespace: "server" }) private create: any;
|
||||
|
||||
private data() {
|
||||
return {
|
||||
columns
|
||||
};
|
||||
}
|
||||
|
||||
private get rooms(): RoomRow[] {
|
||||
if (this.server.rooms == null) {
|
||||
return [];
|
||||
}
|
||||
let rooms: RoomRow[] = [];
|
||||
for (const room of this.server.rooms) {
|
||||
rooms.push({
|
||||
id: room.id,
|
||||
game_id: room.game_id,
|
||||
name: room.name,
|
||||
creator: room.creator,
|
||||
players: room.current_players ? room.current_players : 0,
|
||||
spectators: room.current_spectators
|
||||
? room.current_spectators
|
||||
: 0,
|
||||
can_spectate: room.can_spectate ? "Yes" : "No"
|
||||
});
|
||||
}
|
||||
return rooms;
|
||||
}
|
||||
|
||||
private rowClicked(row: RoomRow) {
|
||||
this.join({
|
||||
roomid: row.id,
|
||||
as_spectator: row.can_spectate == "Yes"
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,88 +0,0 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<b-table striped hoverable :data="rows" :columns="columns"></b-table>
|
||||
<br />
|
||||
<b-field grouped>
|
||||
<b-select v-model="target" placeholder="Target">
|
||||
<option value="@channel">
|
||||
Channel
|
||||
</option>
|
||||
</b-select>
|
||||
<b-input v-model="text" placeholder="Message" expanded></b-input>
|
||||
<p class="control">
|
||||
<button @click="sendTxt" class="button is-primary">Send</button>
|
||||
</p>
|
||||
</b-field>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-property-decorator";
|
||||
import { State, Getter } from "vuex-class";
|
||||
import { RoomState } from "@/store/room";
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: "time",
|
||||
label: "Time",
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
field: "type",
|
||||
label: "Type",
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
field: "message",
|
||||
label: "Message"
|
||||
}
|
||||
];
|
||||
|
||||
interface LogRow {
|
||||
time: string;
|
||||
type: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
@Component({})
|
||||
export default class RoomLog extends Vue {
|
||||
@State("room") private room!: RoomState;
|
||||
|
||||
private target!: "@channel" | string;
|
||||
private text!: string;
|
||||
|
||||
private data() {
|
||||
return {
|
||||
columns,
|
||||
target: "@channel",
|
||||
text: ""
|
||||
};
|
||||
}
|
||||
|
||||
private get rows(): LogRow[] {
|
||||
return this.room.messages.map(v => {
|
||||
let fullType = v.type;
|
||||
let msg = "* no message *";
|
||||
if (v.type == "message" && v.message) {
|
||||
fullType += "/" + v.message.type;
|
||||
if (v.message.message && v.message.message != "") {
|
||||
msg = v.message.message;
|
||||
}
|
||||
}
|
||||
if (v.type == "event" && v.event) {
|
||||
fullType += "/" + v.event.type;
|
||||
if (v.event.message && v.event.message != "") {
|
||||
msg = v.event.message;
|
||||
}
|
||||
}
|
||||
return {
|
||||
time: v.time,
|
||||
type: fullType,
|
||||
message: msg
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private sendTxt() {}
|
||||
}
|
||||
</script>
|
|
@ -1,14 +0,0 @@
|
|||
import Vue from "vue";
|
||||
import App from "./App.vue";
|
||||
import store from "./store/index";
|
||||
import Buefy from "buefy";
|
||||
import "./assets/scss/app.scss";
|
||||
|
||||
Vue.use(Buefy);
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount("#app");
|
|
@ -1,75 +0,0 @@
|
|||
import { Room, RoomServerMessage, RoomMessage } from "./store/room";
|
||||
|
||||
export interface RoomConnectionData {
|
||||
ws_url: string;
|
||||
auth_token: string;
|
||||
}
|
||||
|
||||
export type MessageHandler = (msg: RoomServerMessage) => void;
|
||||
|
||||
export default class RoomClient {
|
||||
private ws: WebSocket;
|
||||
public info: Room;
|
||||
public backlog: RoomServerMessage[];
|
||||
|
||||
private onMessage?: MessageHandler;
|
||||
private buffer: RoomServerMessage[];
|
||||
|
||||
constructor(_ws: WebSocket, _info: Room) {
|
||||
this.info = _info;
|
||||
this.buffer = [];
|
||||
this.backlog = [];
|
||||
this.ws = _ws;
|
||||
this.ws.addEventListener("message", this._received.bind(this));
|
||||
}
|
||||
|
||||
private _received(ev: MessageEvent) {
|
||||
let data = JSON.parse(ev.data);
|
||||
if (this.onMessage) {
|
||||
return this.onMessage(data);
|
||||
}
|
||||
// Save messages in a buffer if no handler is set
|
||||
this.buffer.push(data);
|
||||
}
|
||||
|
||||
public setMessageHandler(handler: MessageHandler) {
|
||||
// Set as handler for all future messages
|
||||
this.onMessage = handler;
|
||||
// If we have messages in our buffer, send them over
|
||||
for (const msg of this.buffer) {
|
||||
handler(msg);
|
||||
}
|
||||
// Empty buffer
|
||||
this.buffer = [];
|
||||
}
|
||||
|
||||
public static connect(wsdata: RoomConnectionData): Promise<RoomClient> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let ws = new WebSocket(wsdata.ws_url);
|
||||
const onMessage = (ev: MessageEvent) => {
|
||||
// Unregister handler
|
||||
ws.removeEventListener("message", onMessage);
|
||||
// Parse message
|
||||
let data = JSON.parse(ev.data) as RoomServerMessage;
|
||||
if ("error" in data) {
|
||||
reject(data.error);
|
||||
}
|
||||
if (data.room) {
|
||||
let client = new RoomClient(ws, data.room);
|
||||
// Check for backlog
|
||||
if (data.backlog) {
|
||||
client.backlog = data.backlog;
|
||||
}
|
||||
resolve(client);
|
||||
} else {
|
||||
reject("missing room info");
|
||||
}
|
||||
};
|
||||
ws.addEventListener("message", onMessage);
|
||||
ws.addEventListener("open", ev => {
|
||||
// Send authentication
|
||||
ws.send(JSON.stringify({ token: wsdata.auth_token }));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
13
webclient/src/shims-tsx.d.ts
vendored
13
webclient/src/shims-tsx.d.ts
vendored
|
@ -1,13 +0,0 @@
|
|||
import Vue, { VNode } from "vue";
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
// tslint:disable no-empty-interface
|
||||
interface Element extends VNode {}
|
||||
// tslint:disable no-empty-interface
|
||||
interface ElementClass extends Vue {}
|
||||
interface IntrinsicElements {
|
||||
[elem: string]: any;
|
||||
}
|
||||
}
|
||||
}
|
4
webclient/src/shims-vue.d.ts
vendored
4
webclient/src/shims-vue.d.ts
vendored
|
@ -1,4 +0,0 @@
|
|||
declare module "*.vue" {
|
||||
import Vue from "vue";
|
||||
export default Vue;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
import Vue from "vue";
|
||||
import Vuex, { StoreOptions } from "vuex";
|
||||
import { server } from "./server";
|
||||
import { room } from "./room";
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export interface AppState {
|
||||
// Client info
|
||||
playerName: string;
|
||||
}
|
||||
|
||||
const store: StoreOptions<AppState> = {
|
||||
state: {
|
||||
playerName:
|
||||
"webclient-" +
|
||||
Math.random()
|
||||
.toString(32)
|
||||
.slice(2)
|
||||
},
|
||||
modules: {
|
||||
server,
|
||||
room
|
||||
}
|
||||
};
|
||||
|
||||
export default new Vuex.Store<AppState>(store);
|
|
@ -1,98 +0,0 @@
|
|||
import { Module, MutationTree, ActionTree, GetterTree } from "vuex";
|
||||
import { AppState } from "@/store/index";
|
||||
import RoomClient from "@/roomclient";
|
||||
|
||||
const namespaced: boolean = true;
|
||||
|
||||
export interface Room {
|
||||
id: string;
|
||||
game_id: string;
|
||||
name: string;
|
||||
creator: string;
|
||||
players: string[];
|
||||
spectators: string[];
|
||||
current_players: number;
|
||||
current_spectators: number;
|
||||
can_spectate: boolean;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export interface RoomEvent {
|
||||
type: string;
|
||||
player?: string;
|
||||
role?: string;
|
||||
room?: Room;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface RoomMessage {
|
||||
from?: string;
|
||||
to?: string;
|
||||
channel: string;
|
||||
type: string;
|
||||
data: Object;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface RoomServerMessage {
|
||||
roomid: string;
|
||||
time: string;
|
||||
type: "event" | "message" | "welcome";
|
||||
event?: RoomEvent;
|
||||
message?: RoomMessage;
|
||||
error?: string;
|
||||
backlog?: RoomServerMessage[];
|
||||
room?: Room;
|
||||
}
|
||||
|
||||
export interface RoomState {
|
||||
in_room: boolean;
|
||||
room: Room | null;
|
||||
client: RoomClient | null;
|
||||
messages: RoomServerMessage[];
|
||||
}
|
||||
|
||||
const state: RoomState = {
|
||||
in_room: false,
|
||||
room: null,
|
||||
client: null,
|
||||
messages: []
|
||||
};
|
||||
|
||||
const mutations: MutationTree<RoomState> = {
|
||||
joinedRoom(state: RoomState, ws: RoomClient) {
|
||||
state.client = ws;
|
||||
state.in_room = true;
|
||||
state.room = ws.info;
|
||||
state.messages = ws.backlog;
|
||||
},
|
||||
messageReceived(state: RoomState, msg: RoomServerMessage) {
|
||||
state.messages.push(msg);
|
||||
}
|
||||
};
|
||||
|
||||
const actions: ActionTree<RoomState, AppState> = {
|
||||
setClient({ commit }, ws: RoomClient) {
|
||||
ws.setMessageHandler(msg => {
|
||||
commit("messageReceived", msg);
|
||||
});
|
||||
commit("joinedRoom", ws);
|
||||
}
|
||||
};
|
||||
|
||||
const getters: GetterTree<RoomState, AppState> = {
|
||||
users(state: RoomState): string[] {
|
||||
if (!state.room) {
|
||||
return [];
|
||||
}
|
||||
return [...state.room.players, ...state.room.spectators];
|
||||
}
|
||||
};
|
||||
|
||||
export const room: Module<RoomState, AppState> = {
|
||||
namespaced,
|
||||
state,
|
||||
mutations,
|
||||
actions,
|
||||
getters
|
||||
};
|
|
@ -1,137 +0,0 @@
|
|||
import { Module, ActionTree, MutationTree, GetterTree } from "vuex";
|
||||
import { AppState } from "@/store/index";
|
||||
import { Room } from "./room";
|
||||
import RoomClient from "@/roomclient";
|
||||
|
||||
const namespaced: boolean = true;
|
||||
|
||||
export interface ServerState {
|
||||
server: string;
|
||||
joining: boolean;
|
||||
connecting: boolean;
|
||||
connected: boolean;
|
||||
rooms: Room[] | null;
|
||||
connectionError: string;
|
||||
joinError: string;
|
||||
}
|
||||
|
||||
const state: ServerState = {
|
||||
connecting: false,
|
||||
joining: false,
|
||||
connected: false,
|
||||
server: "",
|
||||
rooms: null,
|
||||
connectionError: "",
|
||||
joinError: ""
|
||||
};
|
||||
|
||||
const mutations: MutationTree<ServerState> = {
|
||||
beginConnection(state: ServerState) {
|
||||
state.connected = false;
|
||||
state.connectionError = "";
|
||||
state.connecting = true;
|
||||
},
|
||||
|
||||
beginJoin(state: ServerState) {
|
||||
state.joining = true;
|
||||
},
|
||||
|
||||
connectionDone(
|
||||
state: ServerState,
|
||||
payload: { addr: string; rooms: Room[] }
|
||||
) {
|
||||
state.connected = true;
|
||||
state.server = payload.addr;
|
||||
state.rooms = payload.rooms;
|
||||
},
|
||||
|
||||
connectionFailed(state: ServerState, err: Error) {
|
||||
state.connecting = false;
|
||||
state.connectionError = err.message;
|
||||
},
|
||||
|
||||
joinFailed(state: ServerState, err: Error) {
|
||||
state.joining = false;
|
||||
state.joinError = err.message;
|
||||
}
|
||||
};
|
||||
|
||||
const actions: ActionTree<ServerState, AppState> = {
|
||||
async connect({ commit }, addr: string) {
|
||||
commit("beginConnection");
|
||||
// Get room list
|
||||
try {
|
||||
let req = await fetch(`${addr}/api/lobby/room/list`);
|
||||
let data = await req.json();
|
||||
commit("connectionDone", { addr, rooms: data.rooms });
|
||||
} catch (err) {
|
||||
commit("connectionFailed", err);
|
||||
}
|
||||
},
|
||||
|
||||
async joinRoom(
|
||||
{ state, commit, dispatch, rootState },
|
||||
{ roomid, as_spectator }
|
||||
) {
|
||||
commit("beginJoin");
|
||||
// Try joining room
|
||||
try {
|
||||
// Ask lobby server for permission
|
||||
let req = await fetch(`${state.server}/api/lobby/room/join`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
room_id: roomid,
|
||||
player_name: rootState.playerName,
|
||||
as_player: !as_spectator
|
||||
})
|
||||
});
|
||||
let data = await req.json();
|
||||
// We haz permission, let's contact the room server
|
||||
let ws = await RoomClient.connect(data);
|
||||
dispatch("room/setClient", ws, { root: true });
|
||||
} catch (err) {
|
||||
commit("joinFailed", err);
|
||||
}
|
||||
},
|
||||
|
||||
async createRoom({ state, commit, dispatch, rootState }) {
|
||||
commit("beginJoin");
|
||||
// Try creating room
|
||||
try {
|
||||
// Ask lobby server for permission
|
||||
let req = await fetch(`${state.server}/api/lobby/room/create`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
game_id: "webclient",
|
||||
player_name: rootState.playerName,
|
||||
name: `${rootState.playerName}'s room`,
|
||||
allow_spectators: true,
|
||||
max_players: 32,
|
||||
max_spectators: 32,
|
||||
tags: ["test"]
|
||||
})
|
||||
});
|
||||
let data = await req.json();
|
||||
// We haz permission, let's contact the room server
|
||||
let ws = await RoomClient.connect(data);
|
||||
dispatch("room/setClient", ws, { root: true });
|
||||
} catch (err) {
|
||||
commit("joinFailed", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const server: Module<ServerState, AppState> = {
|
||||
namespaced,
|
||||
state,
|
||||
actions,
|
||||
mutations
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": ["webpack-env"],
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
8555
webclient/yarn.lock
8555
webclient/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue