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.
|
Tools and services needed for the MCG backend to work.
|
||||||
|
|
||||||
|
## Inside this repository
|
||||||
|
|
||||||
### draftbot
|
### draftbot
|
||||||
|
|
||||||
Bot for drafting MLP:CCG sets and cubes on MCG
|
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
|
### buildmap / convertsets / genpics
|
||||||
|
|
||||||
Tools for converting data/image packs from OCTGN to MCG/Cardgage
|
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