game flow
This commit is contained in:
parent
c40dcd74db
commit
9a6f430e4d
2
.env
Normal file
2
.env
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
VITE_LOG_LEVEL= 'debug'
|
||||||
|
VITE_API_URL= 'http://localhost:3000/api'
|
260
package-lock.json
generated
260
package-lock.json
generated
@ -9,7 +9,10 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bulma": "^1.0.1",
|
"bulma": "^1.0.1",
|
||||||
|
"colorette": "^2.0.20",
|
||||||
|
"dayjs": "^1.11.11",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
|
"pino": "^9.2.0",
|
||||||
"pixi-filters": "^6.0.4",
|
"pixi-filters": "^6.0.4",
|
||||||
"pixi.js": "^8.2.1",
|
"pixi.js": "^8.2.1",
|
||||||
"socket.io-client": "^4.7.5",
|
"socket.io-client": "^4.7.5",
|
||||||
@ -1492,6 +1495,17 @@
|
|||||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/abort-controller": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||||
|
"dependencies": {
|
||||||
|
"event-target-shim": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.12.0",
|
"version": "8.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
|
||||||
@ -1620,12 +1634,39 @@
|
|||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/atomic-sleep": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/base64-js": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
@ -1665,6 +1706,29 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bulma": {
|
"node_modules/bulma": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bulma/-/bulma-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/bulma/-/bulma-1.0.1.tgz",
|
||||||
@ -1798,6 +1862,11 @@
|
|||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/colorette": {
|
||||||
|
"version": "2.0.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||||
|
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
|
||||||
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
@ -1909,6 +1978,11 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dayjs": {
|
||||||
|
"version": "1.11.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz",
|
||||||
|
"integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg=="
|
||||||
|
},
|
||||||
"node_modules/de-indent": {
|
"node_modules/de-indent": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||||
@ -2370,11 +2444,27 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/event-target-shim": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eventemitter3": {
|
"node_modules/eventemitter3": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
|
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/events": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/execa": {
|
"node_modules/execa": {
|
||||||
"version": "8.0.1",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
|
||||||
@ -2450,6 +2540,14 @@
|
|||||||
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
|
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-redact": {
|
||||||
|
"version": "3.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
|
||||||
|
"integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.17.1",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
||||||
@ -2751,6 +2849,25 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ieee754": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
|
||||||
@ -3391,6 +3508,14 @@
|
|||||||
"integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==",
|
"integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/on-exit-leak-free": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/once": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@ -3649,6 +3774,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pino": {
|
||||||
|
"version": "9.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino/-/pino-9.2.0.tgz",
|
||||||
|
"integrity": "sha512-g3/hpwfujK5a4oVbaefoJxezLzsDgLcNJeITvC6yrfwYeT9la+edCK42j5QpEQSQCZgTKapXvnQIdgZwvRaZug==",
|
||||||
|
"dependencies": {
|
||||||
|
"atomic-sleep": "^1.0.0",
|
||||||
|
"fast-redact": "^3.1.1",
|
||||||
|
"on-exit-leak-free": "^2.1.0",
|
||||||
|
"pino-abstract-transport": "^1.2.0",
|
||||||
|
"pino-std-serializers": "^7.0.0",
|
||||||
|
"process-warning": "^3.0.0",
|
||||||
|
"quick-format-unescaped": "^4.0.3",
|
||||||
|
"real-require": "^0.2.0",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"sonic-boom": "^4.0.1",
|
||||||
|
"thread-stream": "^3.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"pino": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pino-abstract-transport": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"readable-stream": "^4.0.0",
|
||||||
|
"split2": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pino-std-serializers": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA=="
|
||||||
|
},
|
||||||
"node_modules/pixi-filters": {
|
"node_modules/pixi-filters": {
|
||||||
"version": "6.0.4",
|
"version": "6.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/pixi-filters/-/pixi-filters-6.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/pixi-filters/-/pixi-filters-6.0.4.tgz",
|
||||||
@ -3789,6 +3949,19 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process": {
|
||||||
|
"version": "0.11.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/process-warning": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ=="
|
||||||
|
},
|
||||||
"node_modules/proto-list": {
|
"node_modules/proto-list": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
||||||
@ -3836,6 +4009,11 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/quick-format-unescaped": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "18.3.1",
|
"version": "18.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||||
@ -3855,6 +4033,21 @@
|
|||||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "4.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
|
||||||
|
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
|
||||||
|
"dependencies": {
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"events": "^3.3.0",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"string_decoder": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
@ -3867,6 +4060,14 @@
|
|||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/real-require": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/requires-port": {
|
"node_modules/requires-port": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
@ -4015,6 +4216,33 @@
|
|||||||
"queue-microtask": "^1.2.2"
|
"queue-microtask": "^1.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/safe-stable-stringify": {
|
||||||
|
"version": "2.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz",
|
||||||
|
"integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/safer-buffer": {
|
"node_modules/safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
@ -4150,6 +4378,14 @@
|
|||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sonic-boom": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"atomic-sleep": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||||
@ -4158,6 +4394,14 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/split2": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/stackback": {
|
"node_modules/stackback": {
|
||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
||||||
@ -4170,6 +4414,14 @@
|
|||||||
"integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==",
|
"integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
@ -4336,6 +4588,14 @@
|
|||||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/thread-stream": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
|
||||||
|
"dependencies": {
|
||||||
|
"real-require": "^0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tiny-emitter": {
|
"node_modules/tiny-emitter": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||||
|
@ -15,7 +15,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bulma": "^1.0.1",
|
"bulma": "^1.0.1",
|
||||||
|
"colorette": "^2.0.20",
|
||||||
|
"dayjs": "^1.11.11",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
|
"pino": "^9.2.0",
|
||||||
"pixi-filters": "^6.0.4",
|
"pixi-filters": "^6.0.4",
|
||||||
"pixi.js": "^8.2.1",
|
"pixi.js": "^8.2.1",
|
||||||
"socket.io-client": "^4.7.5",
|
"socket.io-client": "^4.7.5",
|
||||||
|
@ -3,8 +3,10 @@ import type { Container } from 'pixi.js'
|
|||||||
export interface PlayerDto {
|
export interface PlayerDto {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
score?: number
|
score: number
|
||||||
hand?: string[]
|
hand: TileDto[]
|
||||||
|
teamedWith: PlayerDto | null
|
||||||
|
ready: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TileDto {
|
export interface TileDto {
|
||||||
@ -16,7 +18,7 @@ export interface TileDto {
|
|||||||
width?: number
|
width?: number
|
||||||
height?: number
|
height?: number
|
||||||
}
|
}
|
||||||
export interface GameSessionState {
|
export interface MatchSessionState {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
creator: string
|
creator: string
|
||||||
@ -46,14 +48,9 @@ export interface GameState {
|
|||||||
tileSelectionPhase: boolean
|
tileSelectionPhase: boolean
|
||||||
boardFreeEnds: number[]
|
boardFreeEnds: number[]
|
||||||
lastMove: Movement
|
lastMove: Movement
|
||||||
}
|
scoreboard: Map<string, number>
|
||||||
|
matchWinner: PlayerDto | null
|
||||||
export interface PlayerState {
|
matchInProgress: boolean
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
score: number
|
|
||||||
hand: TileDto[]
|
|
||||||
teamedWith: string | undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Movement {
|
export interface Movement {
|
||||||
@ -75,3 +72,10 @@ export interface ContainerOptions {
|
|||||||
visible?: boolean
|
visible?: boolean
|
||||||
parent?: Container
|
parent?: Container
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Dimension {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
}
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { GameSessionState, GameState, PlayerState } from '@/common/interfaces'
|
import type { MatchSessionState, GameState, PlayerDto } from '@/common/interfaces'
|
||||||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
import { onMounted, onUnmounted, ref, watch, inject } from 'vue'
|
||||||
import { Game } from '@/game/Game'
|
import { Game } from '@/game/Game'
|
||||||
import { useGameStore } from '@/stores/game'
|
import { useGameStore } from '@/stores/game'
|
||||||
|
import { useEventBusStore } from '@/stores/eventBus'
|
||||||
|
import type { LoggingService } from '@/services/LoggingService'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
|
const logger: LoggingService = inject<LoggingService>('logger') as LoggingService
|
||||||
const emit = defineEmits(['move'])
|
const emit = defineEmits(['move'])
|
||||||
const props = defineProps({
|
const socketService: any = inject('socket')
|
||||||
playerId: String
|
|
||||||
})
|
|
||||||
|
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
|
const eventBus = useEventBusStore()
|
||||||
|
const { playerState, sessionState } = storeToRefs(gameStore)
|
||||||
|
|
||||||
let appEl = ref<HTMLElement | null>(null)
|
let appEl = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
const game = new Game(
|
const game = new Game(
|
||||||
{
|
{
|
||||||
width: 1200,
|
width: 1200,
|
||||||
@ -20,38 +25,9 @@ const game = new Game(
|
|||||||
handScale: 1
|
handScale: 1
|
||||||
},
|
},
|
||||||
emit,
|
emit,
|
||||||
props
|
socketService,
|
||||||
)
|
playerState.value?.id || '',
|
||||||
|
sessionState.value?.id || ''
|
||||||
watch(
|
|
||||||
() => gameStore.canMakeMove,
|
|
||||||
(value: boolean) => {
|
|
||||||
game.setCanMakeMove(value)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
watch(
|
|
||||||
() => gameStore.gameState,
|
|
||||||
(value: GameState | undefined) => {
|
|
||||||
if (value === undefined) return
|
|
||||||
game.board.setState(value, props.playerId ?? '')
|
|
||||||
// console.log('gameState-------------------------------------- :>> ', value)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => gameStore.sessionState,
|
|
||||||
(value: GameSessionState | undefined) => {
|
|
||||||
if (value === undefined) return
|
|
||||||
// console.log('gameSessionState-------------------------------------- :>> ', value)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => gameStore.playerState,
|
|
||||||
(value: PlayerState | undefined) => {
|
|
||||||
if (value === undefined) return
|
|
||||||
game.hand.update(value as PlayerState)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@ -61,6 +37,42 @@ onMounted(async () => {
|
|||||||
await game.preload()
|
await game.preload()
|
||||||
await game.start()
|
await game.start()
|
||||||
|
|
||||||
|
eventBus.subscribe('game-finished', () => {
|
||||||
|
game.gameFinished()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => gameStore.canMakeMove,
|
||||||
|
(value: boolean) => {
|
||||||
|
game.setCanMakeMove(value)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => gameStore.gameState,
|
||||||
|
(value: GameState | undefined) => {
|
||||||
|
if (value === undefined) return
|
||||||
|
logger.debug('gameState-------------------------------------- :>> ', value)
|
||||||
|
game.board?.setState(value, playerState.value?.id ?? '')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => gameStore.sessionState,
|
||||||
|
(value: MatchSessionState | undefined) => {
|
||||||
|
if (value === undefined) return
|
||||||
|
// logger.debug('gameSessionState-------------------------------------- :>> ', value)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => gameStore.playerState,
|
||||||
|
(value: PlayerDto | undefined) => {
|
||||||
|
if (value === undefined) return
|
||||||
|
game.hand.update(value as PlayerDto)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// mockMove(game, [6, 6], 'left')
|
// mockMove(game, [6, 6], 'left')
|
||||||
// mockMove(game, [6, 4], 'left')
|
// mockMove(game, [6, 4], 'left')
|
||||||
// mockMove(game, [4, 4], 'left')
|
// mockMove(game, [4, 4], 'left')
|
||||||
|
@ -14,10 +14,13 @@ import { Tile } from '@/game/Tile'
|
|||||||
import { DIRECTIONS, createContainer, isTilePair } from '@/common/helpers'
|
import { DIRECTIONS, createContainer, isTilePair } from '@/common/helpers'
|
||||||
import { createText } from '@/game/utilities/fonts'
|
import { createText } from '@/game/utilities/fonts'
|
||||||
import { Dot } from '@/game/Dot'
|
import { Dot } from '@/game/Dot'
|
||||||
|
import { LoggingService } from '@/services/LoggingService'
|
||||||
|
import { inject } from 'vue'
|
||||||
|
|
||||||
export class Board extends EventEmitter {
|
export class Board extends EventEmitter {
|
||||||
private _scale: number = 1
|
private _scale: number = 1
|
||||||
private _canMove: boolean = false
|
private _canMove: boolean = false
|
||||||
|
private logger = inject<LoggingService>('logger')!
|
||||||
|
|
||||||
ticker: Ticker
|
ticker: Ticker
|
||||||
height: number
|
height: number
|
||||||
@ -25,7 +28,7 @@ export class Board extends EventEmitter {
|
|||||||
grain: number = 25
|
grain: number = 25
|
||||||
scaleY: ScaleFunction
|
scaleY: ScaleFunction
|
||||||
scaleX: ScaleFunction
|
scaleX: ScaleFunction
|
||||||
state: GameState | undefined
|
state?: GameState
|
||||||
container!: Container
|
container!: Container
|
||||||
initialContainer!: Container
|
initialContainer!: Container
|
||||||
tilesContainer!: Container
|
tilesContainer!: Container
|
||||||
@ -45,7 +48,7 @@ export class Board extends EventEmitter {
|
|||||||
leftDirection: string = 'west'
|
leftDirection: string = 'west'
|
||||||
rightDirection: string = 'east'
|
rightDirection: string = 'east'
|
||||||
playerHand: Tile[] = []
|
playerHand: Tile[] = []
|
||||||
firstTile: Tile | undefined
|
firstTile?: Tile
|
||||||
|
|
||||||
constructor(app: Application) {
|
constructor(app: Application) {
|
||||||
super()
|
super()
|
||||||
@ -63,8 +66,8 @@ export class Board extends EventEmitter {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const background = new Sprite(Assets.get('bg-1'))
|
const background = new Sprite(Assets.get('bg-1'))
|
||||||
background.width = this.width
|
// background.width = this.width
|
||||||
background.height = this.height
|
// background.height = this.height
|
||||||
this.container.addChild(background)
|
this.container.addChild(background)
|
||||||
|
|
||||||
this.initialContainer = createContainer({
|
this.initialContainer = createContainer({
|
||||||
@ -102,7 +105,13 @@ export class Board extends EventEmitter {
|
|||||||
this.tilesContainer.addChild(verticalLine)
|
this.tilesContainer.addChild(verticalLine)
|
||||||
this.tilesContainer.addChild(horizontalLine)
|
this.tilesContainer.addChild(horizontalLine)
|
||||||
|
|
||||||
this.createTexts()
|
this.textContainer = createContainer({
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
parent: this.container
|
||||||
|
})
|
||||||
|
|
||||||
|
this.showText('Starting game...')
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateScale() {
|
private calculateScale() {
|
||||||
@ -138,20 +147,17 @@ export class Board extends EventEmitter {
|
|||||||
this.playerHand = tiles
|
this.playerHand = tiles
|
||||||
}
|
}
|
||||||
|
|
||||||
createTexts() {
|
showText(text: string) {
|
||||||
this.textContainer = new Container()
|
this.textContainer.removeChildren()
|
||||||
this.textWaitForPlayers = createText('Waiting for players', this.scaleX(0), 100)
|
this.textContainer.addChild(createText(text, this.scaleX(0), 100))
|
||||||
this.textYourTurn = createText('Your turn!', this.scaleX(0), 100)
|
|
||||||
|
|
||||||
this.container.addChild(this.textContainer)
|
|
||||||
this.textContainer.addChild(this.textWaitForPlayers)
|
|
||||||
this.textContainer.addChild(this.textYourTurn)
|
|
||||||
this.textYourTurn.visible = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateCanMoveText() {
|
private updateCanMoveText() {
|
||||||
this.textWaitForPlayers.visible = !this.canMove
|
if (this.canMove) {
|
||||||
this.textYourTurn.visible = this.canMove
|
this.showText('Your turn!')
|
||||||
|
} else {
|
||||||
|
this.showText('Waiting for players')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(state: GameState, playerId: string) {
|
setState(state: GameState, playerId: string) {
|
||||||
@ -161,7 +167,6 @@ export class Board extends EventEmitter {
|
|||||||
if (lastMove === null) {
|
if (lastMove === null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log('lastMove :>> ', lastMove)
|
|
||||||
if (
|
if (
|
||||||
lastMove !== null &&
|
lastMove !== null &&
|
||||||
lastMove.tile !== undefined &&
|
lastMove.tile !== undefined &&
|
||||||
@ -319,7 +324,6 @@ export class Board extends EventEmitter {
|
|||||||
y += isEndVertical && !isNextVertical ? 0 : 1
|
y += isEndVertical && !isNextVertical ? 0 : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log('position::>>', tile.pips, x, y)
|
|
||||||
tile.setPosition(this.scaleX(x), this.scaleY(y))
|
tile.setPosition(this.scaleX(x), this.scaleY(y))
|
||||||
tile.setOrientation(orientation)
|
tile.setOrientation(orientation)
|
||||||
tile.reScale(this.scale)
|
tile.reScale(this.scale)
|
||||||
@ -386,7 +390,7 @@ export class Board extends EventEmitter {
|
|||||||
this.addTile(tile, move)
|
this.addTile(tile, move)
|
||||||
this.setFreeEnd(move)
|
this.setFreeEnd(move)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('error :>> ', error)
|
this.logger.error(error, 'Error updating board')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,7 +399,6 @@ export class Board extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setValidEnds(values: boolean[], tile: TileDto) {
|
setValidEnds(values: boolean[], tile: TileDto) {
|
||||||
console.log('validEnds')
|
|
||||||
if (this.count === 0) {
|
if (this.count === 0) {
|
||||||
this.createInteractionsII('right', [[0, 0], undefined, undefined, undefined])
|
this.createInteractionsII('right', [[0, 0], undefined, undefined, undefined])
|
||||||
return
|
return
|
||||||
@ -405,15 +408,12 @@ export class Board extends EventEmitter {
|
|||||||
const side = 'left'
|
const side = 'left'
|
||||||
const validInteractions = this.nextTileValidMoves(tile, side)
|
const validInteractions = this.nextTileValidMoves(tile, side)
|
||||||
const validPoints = this.nextTileValidPoints(tile, side, validInteractions)
|
const validPoints = this.nextTileValidPoints(tile, side, validInteractions)
|
||||||
console.log('validInteractions :>> ', validInteractions)
|
|
||||||
// this.createInteractions(side, tile)
|
|
||||||
this.createInteractionsII(side, validPoints)
|
this.createInteractionsII(side, validPoints)
|
||||||
}
|
}
|
||||||
if (values[1]) {
|
if (values[1]) {
|
||||||
const side = 'right'
|
const side = 'right'
|
||||||
const validInteractions = this.nextTileValidMoves(tile, side)
|
const validInteractions = this.nextTileValidMoves(tile, side)
|
||||||
const validPoints = this.nextTileValidPoints(tile, side, validInteractions)
|
const validPoints = this.nextTileValidPoints(tile, side, validInteractions)
|
||||||
console.log('validInteractions :>> ', validInteractions)
|
|
||||||
this.createInteractionsII(side, validPoints)
|
this.createInteractionsII(side, validPoints)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -501,7 +501,6 @@ export class Board extends EventEmitter {
|
|||||||
dot.alpha = 0.5
|
dot.alpha = 0.5
|
||||||
dot.interactive = true
|
dot.interactive = true
|
||||||
dot.on('pointerdown', () => {
|
dot.on('pointerdown', () => {
|
||||||
console.log('direction :>> ', direction)
|
|
||||||
this.emit(`${side}Click`, direction && { direction, x, y })
|
this.emit(`${side}Click`, direction && { direction, x, y })
|
||||||
this.cleanInteractions()
|
this.cleanInteractions()
|
||||||
})
|
})
|
||||||
@ -567,4 +566,21 @@ export class Board extends EventEmitter {
|
|||||||
}
|
}
|
||||||
return [canPlayNorth, canPlayEast, canPlaySouth, canPlayWest]
|
return [canPlayNorth, canPlayEast, canPlaySouth, canPlayWest]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gameFinished() {
|
||||||
|
this.tiles = []
|
||||||
|
this.boneyard = []
|
||||||
|
this.movements = []
|
||||||
|
this.playerHand = []
|
||||||
|
this.freeEnds = undefined
|
||||||
|
this.leftTile = undefined
|
||||||
|
this.rightTile = undefined
|
||||||
|
this.nextTile = undefined
|
||||||
|
this.leftDirection = 'west'
|
||||||
|
this.rightDirection = 'east'
|
||||||
|
this.firstTile = undefined
|
||||||
|
this.tilesContainer.removeChildren()
|
||||||
|
this.interactionContainer.removeChildren()
|
||||||
|
this.showText('Game finished')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import { assets } from '@/game/utilities/assets'
|
|||||||
import { Tile } from '@/game/Tile'
|
import { Tile } from '@/game/Tile'
|
||||||
import { Hand } from '@/game/Hand'
|
import { Hand } from '@/game/Hand'
|
||||||
import type { Movement, TileDto } from '@/common/interfaces'
|
import type { Movement, TileDto } from '@/common/interfaces'
|
||||||
|
import type { SocketIoClientService } from '@/services/SocketIoClientService'
|
||||||
|
|
||||||
export class Game {
|
export class Game {
|
||||||
public board!: Board
|
public board!: Board
|
||||||
@ -19,7 +20,9 @@ export class Game {
|
|||||||
height: 650
|
height: 650
|
||||||
},
|
},
|
||||||
private emit: any,
|
private emit: any,
|
||||||
private props: any
|
private socketService: SocketIoClientService,
|
||||||
|
private playerId: string,
|
||||||
|
private sessionId: string
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async setup(): Promise<HTMLCanvasElement> {
|
async setup(): Promise<HTMLCanvasElement> {
|
||||||
@ -66,11 +69,18 @@ export class Game {
|
|||||||
const move: Movement = {
|
const move: Movement = {
|
||||||
id: '',
|
id: '',
|
||||||
type: 'pass',
|
type: 'pass',
|
||||||
playerId: this.props.playerId ?? ''
|
playerId: this.playerId
|
||||||
}
|
}
|
||||||
this.emit('move', move)
|
this.emit('move', move)
|
||||||
this.board.updateBoard(move)
|
this.board.updateBoard(move)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.hand.on('nextClick', async () => {
|
||||||
|
await this.socketService.sendMessageWithAck('playerReady', {
|
||||||
|
user: this.playerId,
|
||||||
|
sessionId: this.sessionId
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getMoves(tile: any): [boolean, boolean] {
|
getMoves(tile: any): [boolean, boolean] {
|
||||||
@ -101,7 +111,7 @@ export class Game {
|
|||||||
const move: Movement = {
|
const move: Movement = {
|
||||||
tile: this.selectedTile,
|
tile: this.selectedTile,
|
||||||
type: 'left',
|
type: 'left',
|
||||||
playerId: this.props.playerId ?? '',
|
playerId: this.playerId,
|
||||||
...data
|
...data
|
||||||
}
|
}
|
||||||
this.emit('move', move)
|
this.emit('move', move)
|
||||||
@ -115,7 +125,7 @@ export class Game {
|
|||||||
const move: Movement = {
|
const move: Movement = {
|
||||||
tile: this.selectedTile,
|
tile: this.selectedTile,
|
||||||
type: 'right',
|
type: 'right',
|
||||||
playerId: this.props.playerId ?? '',
|
playerId: this.playerId,
|
||||||
...data
|
...data
|
||||||
}
|
}
|
||||||
this.emit('move', move)
|
this.emit('move', move)
|
||||||
@ -124,6 +134,11 @@ export class Game {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gameFinished() {
|
||||||
|
this.hand.gameFinished()
|
||||||
|
this.board.gameFinished()
|
||||||
|
}
|
||||||
|
|
||||||
private removeBoardEvents() {
|
private removeBoardEvents() {
|
||||||
this.board.off('leftClick')
|
this.board.off('leftClick')
|
||||||
this.board.off('rightClick')
|
this.board.off('rightClick')
|
||||||
|
@ -9,13 +9,16 @@ import {
|
|||||||
Ticker
|
Ticker
|
||||||
} from 'pixi.js'
|
} from 'pixi.js'
|
||||||
import { Tile } from '@/game/Tile'
|
import { Tile } from '@/game/Tile'
|
||||||
import type { PlayerState, TileDto } from '@/common/interfaces'
|
import type { Dimension, PlayerDto, TileDto } from '@/common/interfaces'
|
||||||
import { GlowFilter } from 'pixi-filters'
|
import { GlowFilter } from 'pixi-filters'
|
||||||
|
import { Scale, type ScaleFunction } from './utilities/scale'
|
||||||
|
import { LoggingService } from '@/services/LoggingService'
|
||||||
|
|
||||||
export class Hand extends EventEmitter {
|
export class Hand extends EventEmitter {
|
||||||
tiles: Tile[] = []
|
tiles: Tile[] = []
|
||||||
container: Container = new Container()
|
container: Container = new Container()
|
||||||
buttonPassContainer: Container = new Container()
|
buttonPass: Container = new Container()
|
||||||
|
buttonNext: Container = new Container()
|
||||||
height: number
|
height: number
|
||||||
width: number
|
width: number
|
||||||
ticker: Ticker
|
ticker: Ticker
|
||||||
@ -24,6 +27,10 @@ export class Hand extends EventEmitter {
|
|||||||
initialized: boolean = false
|
initialized: boolean = false
|
||||||
_canMove: boolean = false
|
_canMove: boolean = false
|
||||||
scale: number = 1
|
scale: number = 1
|
||||||
|
scaleY!: ScaleFunction
|
||||||
|
scaleX!: ScaleFunction
|
||||||
|
grain: number = 25
|
||||||
|
logger: LoggingService = new LoggingService()
|
||||||
|
|
||||||
constructor(app: Application) {
|
constructor(app: Application) {
|
||||||
super()
|
super()
|
||||||
@ -34,24 +41,47 @@ export class Hand extends EventEmitter {
|
|||||||
this.container.y = app.canvas.height - this.height
|
this.container.y = app.canvas.height - this.height
|
||||||
this.container.width = this.width
|
this.container.width = this.width
|
||||||
this.container.height = this.height
|
this.container.height = this.height
|
||||||
|
this.calculateScale()
|
||||||
this.addBg()
|
this.addBg()
|
||||||
this.createPassButton()
|
}
|
||||||
|
|
||||||
|
gameFinished() {
|
||||||
|
this.logger.debug('gameFinished')
|
||||||
|
this.tiles = []
|
||||||
|
this.container.removeChildren()
|
||||||
|
this.initialized = false
|
||||||
|
this.buttonNext = this.createButton(
|
||||||
|
'NEXT',
|
||||||
|
{ x: this.width / 2 - 25, y: this.height / 2, width: 50, height: 20 },
|
||||||
|
'nextClick'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
get canMove() {
|
get canMove() {
|
||||||
return this._canMove
|
return this._canMove
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private calculateScale() {
|
||||||
|
const scaleXSteps = Math.floor(this.width / (this.grain * this.scale)) / 2
|
||||||
|
const scaleYSteps = Math.floor(this.height / (this.grain * this.scale)) / 2
|
||||||
|
this.scaleX = Scale([-scaleXSteps, scaleXSteps], [0, this.width])
|
||||||
|
this.scaleY = Scale([-scaleYSteps, scaleYSteps], [0, this.height])
|
||||||
|
}
|
||||||
|
|
||||||
set canMove(value: boolean) {
|
set canMove(value: boolean) {
|
||||||
this._canMove = value
|
this._canMove = value
|
||||||
this.buttonPassContainer.eventMode = value ? 'static' : 'none'
|
if (value) {
|
||||||
this.buttonPassContainer.cursor = value ? 'pointer' : 'default'
|
this.createPassButton()
|
||||||
|
} else {
|
||||||
|
this.container.removeChild(this.buttonPass)
|
||||||
|
}
|
||||||
|
|
||||||
this.tiles.forEach((tile) => {
|
this.tiles.forEach((tile) => {
|
||||||
tile.interactive = value
|
tile.interactive = value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(playerState: PlayerState) {
|
initialize(playerState: PlayerDto) {
|
||||||
this.tiles = this.createTiles(playerState)
|
this.tiles = this.createTiles(playerState)
|
||||||
this.emit('handUpdated', this.tiles)
|
this.emit('handUpdated', this.tiles)
|
||||||
this.initialized = this.tiles.length > 0
|
this.initialized = this.tiles.length > 0
|
||||||
@ -106,11 +136,15 @@ export class Hand extends EventEmitter {
|
|||||||
tile.off('pointerout')
|
tile.off('pointerout')
|
||||||
}
|
}
|
||||||
|
|
||||||
private createPassButton() {
|
private createButton(
|
||||||
const rectangle = new Graphics().roundRect(0, 0, 80, 30, 10).fill(0xffff00)
|
textStr: string,
|
||||||
|
dimension: Dimension,
|
||||||
|
action: string | Function
|
||||||
|
): Container {
|
||||||
|
const { x, y, width, height } = dimension
|
||||||
|
const rectangle = new Graphics().roundRect(x, y, width + 4, height + 4, 5).fill(0xffff00)
|
||||||
const text = new Text({
|
const text = new Text({
|
||||||
text: 'PASS',
|
text: textStr,
|
||||||
style: {
|
style: {
|
||||||
fontFamily: 'Arial',
|
fontFamily: 'Arial',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@ -120,38 +154,45 @@ export class Hand extends EventEmitter {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
text.anchor = 0.5
|
text.anchor = 0.5
|
||||||
|
const container = new Container()
|
||||||
|
container.addChild(rectangle)
|
||||||
|
container.addChild(text)
|
||||||
|
|
||||||
this.buttonPassContainer = new Container()
|
text.y = y + height / 2
|
||||||
|
text.x = x + width / 2
|
||||||
|
|
||||||
this.buttonPassContainer.addChild(rectangle)
|
container.eventMode = 'static'
|
||||||
this.buttonPassContainer.addChild(text)
|
container.cursor = 'pointer'
|
||||||
|
|
||||||
text.y = this.buttonPassContainer.height / 2 - 4
|
|
||||||
text.x = this.buttonPassContainer.width / 2 - 8
|
|
||||||
|
|
||||||
this.buttonPassContainer.eventMode = 'none'
|
|
||||||
this.buttonPassContainer.cursor = 'default'
|
|
||||||
this.buttonPassContainer.x = 20
|
|
||||||
this.buttonPassContainer.y = this.height / 2 - 10
|
|
||||||
rectangle.alpha = 0.7
|
rectangle.alpha = 0.7
|
||||||
text.alpha = 0.7
|
text.alpha = 0.7
|
||||||
this.buttonPassContainer.on('pointerdown', () => {
|
container.on('pointerdown', () => {
|
||||||
this.emit('passClick')
|
action instanceof Function ? action() : this.emit(action)
|
||||||
})
|
})
|
||||||
this.buttonPassContainer.on('pointerover', () => {
|
container.on('pointerover', () => {
|
||||||
rectangle.alpha = 1
|
rectangle.alpha = 1
|
||||||
text.alpha = 1
|
text.alpha = 1
|
||||||
})
|
})
|
||||||
|
|
||||||
this.buttonPassContainer.on('pointerout', () => {
|
container.on('pointerout', () => {
|
||||||
rectangle.alpha = 0.7
|
rectangle.alpha = 0.7
|
||||||
text.alpha = 0.7
|
text.alpha = 0.7
|
||||||
})
|
})
|
||||||
|
|
||||||
this.container.addChild(this.buttonPassContainer)
|
this.container.addChild(container)
|
||||||
|
return container
|
||||||
}
|
}
|
||||||
|
|
||||||
update(playerState: PlayerState) {
|
private createPassButton() {
|
||||||
|
const lastTile = this.tiles[this.tiles.length - 1]
|
||||||
|
const x = lastTile ? lastTile.x + lastTile.width : this.scaleX(0)
|
||||||
|
this.buttonPass = this.createButton(
|
||||||
|
'PASS',
|
||||||
|
{ x, y: this.height / 2, width: 50, height: 20 },
|
||||||
|
'passClick'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
update(playerState: PlayerDto) {
|
||||||
if (!this.initialized) {
|
if (!this.initialized) {
|
||||||
this.initialize(playerState)
|
this.initialize(playerState)
|
||||||
return
|
return
|
||||||
@ -167,7 +208,7 @@ export class Hand extends EventEmitter {
|
|||||||
this.renderTiles()
|
this.renderTiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
private createTiles(playerState: PlayerState) {
|
private createTiles(playerState: PlayerDto) {
|
||||||
return playerState.hand.map((tile) => {
|
return playerState.hand.map((tile) => {
|
||||||
const newTile: Tile = new Tile(tile.id, this.ticker, tile.pips, this.scale)
|
const newTile: Tile = new Tile(tile.id, this.ticker, tile.pips, this.scale)
|
||||||
newTile.alpha = 0.7
|
newTile.alpha = 0.7
|
||||||
|
@ -9,6 +9,8 @@ import App from './App.vue'
|
|||||||
import router from './router'
|
import router from './router'
|
||||||
|
|
||||||
import { SocketIoClientService } from '@/services/SocketIoClientService'
|
import { SocketIoClientService } from '@/services/SocketIoClientService'
|
||||||
|
import { LoggingService } from '@/services/LoggingService'
|
||||||
|
import { AuthenticationService } from './services/AuthenticationService'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
@ -16,5 +18,7 @@ app.use(createPinia())
|
|||||||
app.use(router)
|
app.use(router)
|
||||||
|
|
||||||
app.provide('socket', new SocketIoClientService('http://localhost:3000'))
|
app.provide('socket', new SocketIoClientService('http://localhost:3000'))
|
||||||
|
app.provide('logger', new LoggingService())
|
||||||
|
app.provide('auth', new AuthenticationService())
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import { useGameStore } from '@/stores/game'
|
import { useGameStore } from '@/stores/game'
|
||||||
import { wait } from '@/common/helpers'
|
import { wait } from '@/common/helpers'
|
||||||
import type { GameSessionState } from '@/common/interfaces'
|
import type { MatchSessionState } from '@/common/interfaces'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { useEventBusStore } from '@/stores/eventBus'
|
||||||
|
|
||||||
export class SocketIoEventManager {
|
export class SocketIoEventManager {
|
||||||
gameStore: any = useGameStore()
|
gameStore: any = useGameStore()
|
||||||
|
eventBus = useEventBusStore()
|
||||||
|
|
||||||
handleSessionStateEvent(data: GameSessionState) {
|
handleSessionStateEvent(data: MatchSessionState) {
|
||||||
const { updateSessionState } = this.gameStore
|
const { updateSessionState } = this.gameStore
|
||||||
updateSessionState(data)
|
updateSessionState(data)
|
||||||
return {
|
return {
|
||||||
@ -55,4 +57,8 @@ export class SocketIoEventManager {
|
|||||||
status: 'ok'
|
status: 'ok'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleGameFinishedEvent() {
|
||||||
|
this.eventBus.publish('game-finished')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ const router = createRouter({
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
const isLoggedIn = !!localStorage.getItem('token')
|
const isLoggedIn = !!sessionStorage.getItem('token')
|
||||||
if (to.matched.some((record) => record.meta.requiresAuth) && !isLoggedIn) {
|
if (to.matched.some((record) => record.meta.requiresAuth) && !isLoggedIn) {
|
||||||
next({ name: 'landing' })
|
next({ name: 'landing' })
|
||||||
} else {
|
} else {
|
||||||
|
61
src/services/AuthenticationService.ts
Normal file
61
src/services/AuthenticationService.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { ServiceBase } from '@/services/ServiceBase'
|
||||||
|
import { useAuthStore } from '@/stores/auth'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { NetworkService } from '@/services/NetworkService'
|
||||||
|
|
||||||
|
export class AuthenticationService extends ServiceBase {
|
||||||
|
private apiUrl = import.meta.env.VITE_API_URL
|
||||||
|
private networkService = new NetworkService()
|
||||||
|
|
||||||
|
isAuthenticated() {
|
||||||
|
const auth = useAuthStore()
|
||||||
|
const { isLoggedIn } = storeToRefs(auth)
|
||||||
|
return isLoggedIn
|
||||||
|
}
|
||||||
|
|
||||||
|
async login(username: string, password: string) {
|
||||||
|
try {
|
||||||
|
const res = await this.networkService.post({
|
||||||
|
uri: '/login',
|
||||||
|
body: { username, password }
|
||||||
|
})
|
||||||
|
const { token } = res
|
||||||
|
this.persist(token)
|
||||||
|
return token
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async logout() {
|
||||||
|
const auth = useAuthStore()
|
||||||
|
const { setJwt, setUser } = auth
|
||||||
|
setJwt(undefined)
|
||||||
|
setUser(undefined)
|
||||||
|
sessionStorage.removeItem('token')
|
||||||
|
}
|
||||||
|
|
||||||
|
private persist(jwt: string) {
|
||||||
|
const auth = useAuthStore()
|
||||||
|
const { setJwt, setUser } = auth
|
||||||
|
const loggedUser = this.parseJwt(jwt)
|
||||||
|
setJwt(jwt)
|
||||||
|
setUser(loggedUser)
|
||||||
|
sessionStorage.setItem('token', jwt)
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseJwt(token: string) {
|
||||||
|
if (!token) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const base64Url = token.split('.')[1] ?? ''
|
||||||
|
const base64 = base64Url.replace('-', '+').replace('_', '/')
|
||||||
|
return JSON.parse(window.atob(base64))
|
||||||
|
}
|
||||||
|
|
||||||
|
hasRoles(rolesToCheck: string[]) {
|
||||||
|
const auth = useAuthStore()
|
||||||
|
const { roles } = storeToRefs(auth)
|
||||||
|
return roles.value.some((role) => rolesToCheck.includes(role))
|
||||||
|
}
|
||||||
|
}
|
86
src/services/LoggingService.ts
Normal file
86
src/services/LoggingService.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { createColors } from 'colorette'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import pino, { type BaseLogger } from 'pino'
|
||||||
|
import { isProxy, toRaw } from 'vue'
|
||||||
|
|
||||||
|
const { blue, cyan, green, red } = createColors({ useColor: true })
|
||||||
|
|
||||||
|
export class LoggingService {
|
||||||
|
private _logger: BaseLogger
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._logger = pino({
|
||||||
|
browser: {
|
||||||
|
asObject: true,
|
||||||
|
transmit: {
|
||||||
|
level: import.meta.env.VITE_LOG_LEVEL || 'error',
|
||||||
|
send: (level, logEvent) => {
|
||||||
|
const { ts, messages } = logEvent
|
||||||
|
const logStr: string[] = [dayjs(ts).format('HH:mm:ss.SSS')]
|
||||||
|
logStr.push(this.colors[level](level.toUpperCase()))
|
||||||
|
const firstMessage = messages.shift()
|
||||||
|
if (firstMessage.type === 'Error') {
|
||||||
|
logStr.push(red(firstMessage.message))
|
||||||
|
logStr.push(red(firstMessage.stack || ''))
|
||||||
|
} else if (typeof firstMessage === 'string') {
|
||||||
|
logStr.push(cyan(firstMessage))
|
||||||
|
} else {
|
||||||
|
messages.unshift(firstMessage)
|
||||||
|
}
|
||||||
|
console.log(`${logStr.join(' ')}:`, ...messages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private get colors(): any {
|
||||||
|
return {
|
||||||
|
info: green,
|
||||||
|
debug: blue,
|
||||||
|
error: red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(message: string, data?: any) {
|
||||||
|
this._logger.debug(message, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
info(message: string, data?: any) {
|
||||||
|
this._logger.info(this._getMessageWidthObject(message, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(message: string, data?: any) {
|
||||||
|
this._logger.warn(this._getMessageWidthObject(message, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
error(error: any, message?: string) {
|
||||||
|
this._logger.error(error, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fatal(message: string, data?: any) {
|
||||||
|
this._logger.fatal(this._getMessageWidthObject(message, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
trace(message: string, data?: any) {
|
||||||
|
this._logger.trace(this._getMessageWidthObject(message, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
object(message: any) {
|
||||||
|
this._logger.info(this._getStringObject(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
_getMessageWidthObject(message: string, data?: any) {
|
||||||
|
if (!data) {
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
return `${message}\n${this._getStringObject(data)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
_getStringObject(data: any): any {
|
||||||
|
if (isProxy(data)) {
|
||||||
|
return this._getStringObject(toRaw(data))
|
||||||
|
}
|
||||||
|
return JSON.stringify(data, null, 2)
|
||||||
|
}
|
||||||
|
}
|
89
src/services/NetworkService.ts
Normal file
89
src/services/NetworkService.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { useAuthStore } from '@/stores/auth'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
|
interface RequestOptions {
|
||||||
|
uri: string
|
||||||
|
params?: Record<string, string>
|
||||||
|
body?: any
|
||||||
|
auth?: boolean
|
||||||
|
method?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NetworkService {
|
||||||
|
private API_URL = import.meta.env.VITE_API_URL
|
||||||
|
private auth = useAuthStore()
|
||||||
|
|
||||||
|
async post(options: RequestOptions) {
|
||||||
|
options.method = 'POST'
|
||||||
|
return await this.request(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(options: RequestOptions) {
|
||||||
|
options.method = 'GET'
|
||||||
|
return await this.request(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
async patch(options: RequestOptions) {
|
||||||
|
options.method = 'PATCH'
|
||||||
|
return await this.request(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(options: RequestOptions) {
|
||||||
|
options.method = 'DELETE'
|
||||||
|
return await this.request(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
async request(options: RequestOptions) {
|
||||||
|
const { uri, params } = options
|
||||||
|
if (!uri) {
|
||||||
|
throw new Error('URL is required')
|
||||||
|
}
|
||||||
|
const fetchOptions = this.getFetchOptions(options)
|
||||||
|
const urlParams = this.getURLParams(params)
|
||||||
|
const res = await fetch(`${this.API_URL}${uri}${urlParams}`, fetchOptions)
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error('Network response was not ok')
|
||||||
|
}
|
||||||
|
const text = await res.text()
|
||||||
|
|
||||||
|
if (text === '') {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
return JSON.parse(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getURLParams(params: any) {
|
||||||
|
if (!params) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const urlParams = new URLSearchParams()
|
||||||
|
Object.keys(params).forEach((key) => urlParams.append(key, params[key]))
|
||||||
|
return `?${urlParams.toString()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
getFetchOptions(opts: RequestOptions): any {
|
||||||
|
const { body, auth, method = 'GET' } = opts
|
||||||
|
const options: any = {
|
||||||
|
method,
|
||||||
|
headers: this.getHeaders({ auth })
|
||||||
|
}
|
||||||
|
if (!['GET', 'HEAD'].includes(method) && body) {
|
||||||
|
options.body = typeof body === 'string' ? body : JSON.stringify(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders({ auth = true }): any {
|
||||||
|
const { jwt } = storeToRefs(this.auth)
|
||||||
|
const headers: any = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
if (auth) {
|
||||||
|
headers.Authorization = jwt
|
||||||
|
}
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
}
|
5
src/services/ServiceBase.ts
Normal file
5
src/services/ServiceBase.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { LoggingService } from './LoggingService'
|
||||||
|
|
||||||
|
export class ServiceBase {
|
||||||
|
protected logger: LoggingService = new LoggingService()
|
||||||
|
}
|
@ -1,11 +1,13 @@
|
|||||||
import type { GameSessionState, GameState, PlayerState } from '@/common/interfaces'
|
import type { MatchSessionState, GameState, PlayerDto } from '@/common/interfaces'
|
||||||
import { io, Socket } from 'socket.io-client'
|
import { io, Socket } from 'socket.io-client'
|
||||||
import { SocketIoEventManager } from '@/managers/SocketIoEventManager'
|
import { SocketIoEventManager } from '@/managers/SocketIoEventManager'
|
||||||
|
import { LoggingService } from './LoggingService'
|
||||||
|
|
||||||
export class SocketIoClientService {
|
export class SocketIoClientService {
|
||||||
public socket: Socket
|
public socket: Socket
|
||||||
private isConnected = false
|
private isConnected = false
|
||||||
private gameEventManager = new SocketIoEventManager()
|
private gameEventManager = new SocketIoEventManager()
|
||||||
|
private logger: LoggingService = new LoggingService()
|
||||||
|
|
||||||
constructor(url: string) {
|
constructor(url: string) {
|
||||||
this.socket = io(url)
|
this.socket = io(url)
|
||||||
@ -37,7 +39,7 @@ export class SocketIoClientService {
|
|||||||
console.log('Failed to reconnect to server')
|
console.log('Failed to reconnect to server')
|
||||||
})
|
})
|
||||||
|
|
||||||
this.socket.on('sessionState', (data: GameSessionState, callback: any) => {
|
this.socket.on('matchState', (data: MatchSessionState, callback: any) => {
|
||||||
callback(this.gameEventManager.handleSessionStateEvent(data))
|
callback(this.gameEventManager.handleSessionStateEvent(data))
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -45,7 +47,8 @@ export class SocketIoClientService {
|
|||||||
callback(this.gameEventManager.handleGameStateEvent(data))
|
callback(this.gameEventManager.handleGameStateEvent(data))
|
||||||
})
|
})
|
||||||
|
|
||||||
this.socket.on('playerState', (data: PlayerState, callback: any) => {
|
this.socket.on('playerState', (data: PlayerDto, callback: any) => {
|
||||||
|
this.logger.debug('playerState', data)
|
||||||
callback(this.gameEventManager.handlePlayerStateEvent(data))
|
callback(this.gameEventManager.handlePlayerStateEvent(data))
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -57,6 +60,11 @@ export class SocketIoClientService {
|
|||||||
callback(await this.gameEventManager.handleCanSelectTileEvent())
|
callback(await this.gameEventManager.handleCanSelectTileEvent())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.socket.on('game-finished', () => {
|
||||||
|
this.logger.debug('game-finished event received')
|
||||||
|
this.gameEventManager.handleGameFinishedEvent()
|
||||||
|
})
|
||||||
|
|
||||||
this.socket.on('ping', () => {
|
this.socket.on('ping', () => {
|
||||||
console.log('Ping received from server')
|
console.log('Ping received from server')
|
||||||
this.socket.emit('pong') // Send pong response
|
this.socket.emit('pong') // Send pong response
|
||||||
|
33
src/stores/auth.ts
Normal file
33
src/stores/auth.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
|
const jwt = ref<string | undefined>(undefined)
|
||||||
|
const user = ref<any | undefined>(undefined)
|
||||||
|
const roles = ref<string[]>([])
|
||||||
|
|
||||||
|
const isLoggedIn = computed(() => jwt.value !== undefined)
|
||||||
|
|
||||||
|
function setJwt(token: string | undefined) {
|
||||||
|
jwt.value = token
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUser(userIn: any | undefined) {
|
||||||
|
user.value = userIn
|
||||||
|
setRoles(userIn?.roles ?? [])
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRoles(rolesIn: string[]) {
|
||||||
|
roles.value = rolesIn
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
jwt,
|
||||||
|
user,
|
||||||
|
roles,
|
||||||
|
isLoggedIn,
|
||||||
|
setJwt,
|
||||||
|
setUser,
|
||||||
|
setRoles
|
||||||
|
}
|
||||||
|
})
|
43
src/stores/eventBus.ts
Normal file
43
src/stores/eventBus.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export const useEventBusStore = defineStore('eventBus', () => {
|
||||||
|
const events = ref<{ [key: string]: { callback: (payload?: any) => void; once: boolean }[] }>({})
|
||||||
|
|
||||||
|
const subscribe = (event: string, callback: (payload?: any) => void) => {
|
||||||
|
if (!events.value[event]) {
|
||||||
|
events.value[event] = []
|
||||||
|
}
|
||||||
|
events.value[event].push({ callback, once: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscribeOnce = (event: string, callback: (payload?: any) => void) => {
|
||||||
|
if (!events.value[event]) {
|
||||||
|
events.value[event] = []
|
||||||
|
}
|
||||||
|
events.value[event].push({ callback, once: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsubscribe = (event: string, callback: (payload?: any) => void) => {
|
||||||
|
if (events.value[event]) {
|
||||||
|
events.value[event] = events.value[event].filter((e) => e.callback !== callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const publish = (event: string, payload?: any) => {
|
||||||
|
if (events.value[event]) {
|
||||||
|
events.value[event] = events.value[event].filter((e) => {
|
||||||
|
e.callback(payload)
|
||||||
|
return !e.once // Retain if it's not a one-time event
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
events,
|
||||||
|
subscribe,
|
||||||
|
subscribeOnce,
|
||||||
|
unsubscribe,
|
||||||
|
publish
|
||||||
|
}
|
||||||
|
})
|
@ -1,17 +1,22 @@
|
|||||||
import { ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import type { GameSessionState, GameState, Movement, PlayerState } from '@/common/interfaces'
|
import type { MatchSessionState, GameState, Movement, PlayerDto } from '@/common/interfaces'
|
||||||
|
|
||||||
export const useGameStore = defineStore('game', () => {
|
export const useGameStore = defineStore('game', () => {
|
||||||
const sessionState = ref<GameSessionState | undefined>(undefined)
|
const sessionState = ref<MatchSessionState | undefined>(undefined)
|
||||||
const gameState = ref<GameState | undefined>(undefined)
|
const gameState = ref<GameState | undefined>(undefined)
|
||||||
const playerState = ref<PlayerState | undefined>(undefined)
|
const playerState = ref<PlayerDto | undefined>(undefined)
|
||||||
const canMakeMove = ref(false)
|
const canMakeMove = ref(false)
|
||||||
const canSelectTile = ref(false)
|
const canSelectTile = ref(false)
|
||||||
|
const gameFinished = ref(false)
|
||||||
|
const readyForStart = ref(false)
|
||||||
const moveToMake = ref<Movement | undefined>(undefined)
|
const moveToMake = ref<Movement | undefined>(undefined)
|
||||||
const incomingFreeEnds = ref<[number, number] | undefined>(undefined)
|
const incomingFreeEnds = ref<[number, number] | undefined>(undefined)
|
||||||
|
const showReadyButton = ref(false)
|
||||||
|
|
||||||
function updateSessionState(newState: GameSessionState) {
|
const isSessionStarted = computed(() => sessionState.value !== undefined)
|
||||||
|
|
||||||
|
function updateSessionState(newState: MatchSessionState) {
|
||||||
sessionState.value = newState
|
sessionState.value = newState
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,7 +24,7 @@ export const useGameStore = defineStore('game', () => {
|
|||||||
gameState.value = newState
|
gameState.value = newState
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePlayerState(newState: PlayerState) {
|
function updatePlayerState(newState: PlayerDto) {
|
||||||
playerState.value = newState
|
playerState.value = newState
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,6 +44,18 @@ export const useGameStore = defineStore('game', () => {
|
|||||||
incomingFreeEnds.value = freeEnds
|
incomingFreeEnds.value = freeEnds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setShowReadyButton(value: boolean) {
|
||||||
|
showReadyButton.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
function setReadyForStart(value: boolean) {
|
||||||
|
readyForStart.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGameFinished(value: boolean) {
|
||||||
|
gameFinished.value = value
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sessionState,
|
sessionState,
|
||||||
gameState,
|
gameState,
|
||||||
@ -47,12 +64,19 @@ export const useGameStore = defineStore('game', () => {
|
|||||||
moveToMake,
|
moveToMake,
|
||||||
incomingFreeEnds,
|
incomingFreeEnds,
|
||||||
canSelectTile,
|
canSelectTile,
|
||||||
|
showReadyButton,
|
||||||
|
readyForStart,
|
||||||
|
gameFinished,
|
||||||
updateSessionState,
|
updateSessionState,
|
||||||
updateGameState,
|
updateGameState,
|
||||||
updatePlayerState,
|
updatePlayerState,
|
||||||
updateCanMakeMove,
|
updateCanMakeMove,
|
||||||
setMoveToMake,
|
setMoveToMake,
|
||||||
setIncomingFreeEnds,
|
setIncomingFreeEnds,
|
||||||
updateCanSelectTile
|
updateCanSelectTile,
|
||||||
|
setShowReadyButton,
|
||||||
|
setReadyForStart,
|
||||||
|
updateGameFinished,
|
||||||
|
isSessionStarted
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -5,28 +5,22 @@ import { storeToRefs } from 'pinia'
|
|||||||
import { inject, onBeforeUnmount, ref } from 'vue'
|
import { inject, onBeforeUnmount, ref } from 'vue'
|
||||||
import { onMounted } from 'vue'
|
import { onMounted } from 'vue'
|
||||||
import useClipboard from 'vue-clipboard3'
|
import useClipboard from 'vue-clipboard3'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const socketService: any = inject('socket')
|
const socketService: any = inject('socket')
|
||||||
let data = ref('')
|
|
||||||
let responseField = ref('')
|
|
||||||
let statusField = ref('')
|
|
||||||
let sessionId = ref('')
|
|
||||||
let seed = ref('')
|
|
||||||
let playerId = ref('')
|
|
||||||
let selectdAction: any = undefined
|
|
||||||
|
|
||||||
const { toClipboard } = useClipboard()
|
const { toClipboard } = useClipboard()
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
const { moveToMake, canMakeMove, sessionState, gameState } = storeToRefs(gameStore)
|
const { moveToMake, canMakeMove, sessionState, gameState, playerState } = storeToRefs(gameStore)
|
||||||
const options = [
|
|
||||||
{ value: 'createSession', default: '{"user": "arhuako"}' },
|
|
||||||
{ value: 'startSession', default: (id: string) => `{"sessionId": "${id}"}` }
|
|
||||||
// { value: 'joinSession', default: '{"user": "pepe", "sessionId": "arhuako"}' },
|
|
||||||
// { value: 'leaveSession', default: '{"user": "pepe", "sessionId": "arhuako"}' },
|
|
||||||
// { value: 'chat message', default: 'chat message' }
|
|
||||||
]
|
|
||||||
|
|
||||||
onMounted(async () => {})
|
onMounted(async () => {
|
||||||
|
startMatch()
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!playerState?.value) {
|
||||||
|
const router = useRouter()
|
||||||
|
router.push({ name: 'home' })
|
||||||
|
}
|
||||||
|
|
||||||
function makeMove(move: any) {
|
function makeMove(move: any) {
|
||||||
moveToMake.value = move
|
moveToMake.value = move
|
||||||
@ -37,73 +31,19 @@ onBeforeUnmount(() => {
|
|||||||
// socketService.disconnect()
|
// socketService.disconnect()
|
||||||
})
|
})
|
||||||
|
|
||||||
function actionSelected() {
|
async function startMatch() {
|
||||||
if (selectdAction.value === 'createSession') {
|
const sessionId = sessionState?.value?.id
|
||||||
responseField.value = ''
|
const seed = sessionState?.value?.seed
|
||||||
} else if (selectdAction.value === 'startSession') {
|
const playerId = playerState?.value?.id
|
||||||
data.value = selectdAction.default(sessionId.value)
|
if (sessionId) {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data.value = selectdAction.default
|
|
||||||
}
|
|
||||||
|
|
||||||
const getMessage = (msg: string) => {
|
|
||||||
if (msg.startsWith('{') && msg.endsWith('}')) return JSON.parse(msg)
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createSession() {
|
|
||||||
const response = await socketService.sendMessageWithAck('createSession', { user: 'arhuako' })
|
|
||||||
sessionId.value = response.sessionId
|
|
||||||
playerId.value = response.playerId
|
|
||||||
}
|
|
||||||
|
|
||||||
async function startSession() {
|
|
||||||
if (sessionId.value) {
|
|
||||||
await socketService.sendMessageWithAck('startSession', {
|
await socketService.sendMessageWithAck('startSession', {
|
||||||
sessionId: sessionId.value,
|
sessionId: sessionId,
|
||||||
seed: seed.value.trim()
|
playerId: playerId,
|
||||||
|
seed: seed?.trim()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function joinSession() {
|
|
||||||
if (sessionId.value) {
|
|
||||||
const response = await socketService.sendMessageWithAck('joinSession', {
|
|
||||||
user: 'pepe',
|
|
||||||
sessionId: sessionId.value
|
|
||||||
})
|
|
||||||
// sessionId.value = response.sessionId
|
|
||||||
playerId.value = response.playerId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendMessage() {
|
|
||||||
if (selectdAction && data.value.trim() !== '') {
|
|
||||||
const response = await socketService.sendMessageWithAck(
|
|
||||||
selectdAction.value,
|
|
||||||
getMessage(data.value.trim())
|
|
||||||
)
|
|
||||||
handleResponse(response)
|
|
||||||
}
|
|
||||||
// socketService.emit('message', data.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResponse(response: any) {
|
|
||||||
if (selectdAction.value === 'createSession') {
|
|
||||||
sessionId.value = response.sessionId
|
|
||||||
playerId.value = response.playerId
|
|
||||||
}
|
|
||||||
|
|
||||||
data.value = ''
|
|
||||||
const responseStr = JSON.stringify(response, null, 2)
|
|
||||||
responseField.value = !responseField.value
|
|
||||||
? responseStr
|
|
||||||
: responseField.value + '\n---\n ' + responseStr
|
|
||||||
selectdAction = undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
function copySeed() {
|
function copySeed() {
|
||||||
if (sessionState?.value?.seed) toClipboard(sessionState.value.seed)
|
if (sessionState?.value?.seed) toClipboard(sessionState.value.seed)
|
||||||
}
|
}
|
||||||
@ -116,18 +56,25 @@ function copySeed() {
|
|||||||
Running: {{ sessionState?.sessionInProgress }} Seed: {{ sessionState?.seed }}
|
Running: {{ sessionState?.sessionInProgress }} Seed: {{ sessionState?.seed }}
|
||||||
<button @click="copySeed">Copy!</button>
|
<button @click="copySeed">Copy!</button>
|
||||||
</p>
|
</p>
|
||||||
<p>FreeEnds: {{ gameState?.boardFreeEnds }} - {{ gameState?.currentPlayer?.name }}</p>
|
<p>
|
||||||
<p v-if="sessionId">SessionID: {{ sessionId }} PlayerID: {{ playerId }}</p>
|
FreeEnds: {{ gameState?.boardFreeEnds }} - Current Player:{{
|
||||||
|
gameState?.currentPlayer?.name
|
||||||
|
}}
|
||||||
|
- Score: {{ gameState?.scoreboard }}
|
||||||
|
</p>
|
||||||
|
<p v-if="sessionState?.id">
|
||||||
|
SessionID: {{ sessionState.id }} PlayerID: {{ playerState?.id }}
|
||||||
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section class="block">
|
<section class="block">
|
||||||
<div class="game-container">
|
<div class="game-container">
|
||||||
<GameComponent :playerId="playerId" :canMakeMove="canMakeMove" @move="makeMove" />
|
<GameComponent :playerId="playerState?.id" :canMakeMove="canMakeMove" @move="makeMove" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="block">
|
<!-- <section class="block">
|
||||||
<div class="fixed-grid has-8-cols">
|
<div class="fixed-grid has-8-cols">
|
||||||
<div class="grid" v-if="!sessionId">
|
<div class="grid" v-if="!sessionState?.id">
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<button style="width: 200px" class="button" @click="createSession">
|
<button style="width: 200px" class="button" @click="createSession">
|
||||||
Create Session
|
Create Session
|
||||||
@ -156,60 +103,8 @@ function copySeed() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1 action-select"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</section> -->
|
||||||
<div class="grid" style="margin-top: 16px; display: none">
|
|
||||||
<div>
|
|
||||||
<!-- <ul id="messages"></ul> -->
|
|
||||||
<form id="form" action="">
|
|
||||||
<div class="action-select select">
|
|
||||||
<select
|
|
||||||
v-model="selectdAction"
|
|
||||||
id="event"
|
|
||||||
autocomplete="off"
|
|
||||||
@change="actionSelected"
|
|
||||||
>
|
|
||||||
<option value="">Select event</option>
|
|
||||||
<option :key="option.value" v-for="option in options" :value="option">
|
|
||||||
{{ option.value }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
<button @click.prevent.stop="sendMessage">Send</button>
|
|
||||||
</div>
|
|
||||||
<!-- <p><input id="room" autocomplete="off" /></p> -->
|
|
||||||
<p>
|
|
||||||
<textarea
|
|
||||||
v-model="data"
|
|
||||||
id="message"
|
|
||||||
autocomplete="off"
|
|
||||||
placeholder="Data"
|
|
||||||
></textarea>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="grid">
|
|
||||||
<div>
|
|
||||||
<textarea
|
|
||||||
:value="responseField"
|
|
||||||
id="response"
|
|
||||||
autocomplete="off"
|
|
||||||
placeholder="Response"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<textarea
|
|
||||||
:value="statusField"
|
|
||||||
id="status"
|
|
||||||
autocomplete="off"
|
|
||||||
placeholder="Game status"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -1,10 +1,57 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { inject, ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import useClipboard from 'vue-clipboard3'
|
||||||
|
|
||||||
|
import { useGameStore } from '@/stores/game'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { LoggingService } from '@/services/LoggingService'
|
||||||
|
|
||||||
|
let seed = ref('')
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const gameStore = useGameStore()
|
||||||
|
const { toClipboard } = useClipboard()
|
||||||
|
const socketService: any = inject('socket')
|
||||||
|
const logger: LoggingService = inject<LoggingService>('logger') as LoggingService
|
||||||
|
|
||||||
function startGame() {
|
const { readyForStart, sessionState, isSessionStarted, playerState } = storeToRefs(gameStore)
|
||||||
router.push({ name: 'game' })
|
|
||||||
|
async function setPlayerReady() {
|
||||||
|
logger.debug('Starting game')
|
||||||
|
if (!sessionState.value) {
|
||||||
|
logger.error('No session found')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await socketService.sendMessageWithAck('playerReady', {
|
||||||
|
user: 'arhuako',
|
||||||
|
sessionId: sessionState.value.id
|
||||||
|
})
|
||||||
|
readyForStart.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createMatch() {
|
||||||
|
logger.debug('Creating match')
|
||||||
|
socketService.sendMessageWithAck('createSession', { user: 'arhuako' })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function joinMatch() {
|
||||||
|
const sessionId = sessionState?.value?.id
|
||||||
|
const playerId = playerState?.value?.id
|
||||||
|
if (sessionId && playerId) {
|
||||||
|
await socketService.sendMessageWithAck('joinSession', {
|
||||||
|
user: 'pepe',
|
||||||
|
sessionId: sessionId
|
||||||
|
})
|
||||||
|
// sessionId.value = response.sessionId
|
||||||
|
// playerId.value = response.playerId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startMatch() {
|
||||||
|
if (sessionState.value && sessionState.value.id) {
|
||||||
|
router.push({ name: 'game' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -14,8 +61,22 @@ function startGame() {
|
|||||||
<h1 class="title is-2">Welcome to the Player's Home Page</h1>
|
<h1 class="title is-2">Welcome to the Player's Home Page</h1>
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<p>This is a protected route.</p>
|
<p>This is a protected route.</p>
|
||||||
|
<p>{{ sessionState || 'No session' }}</p>
|
||||||
|
<p>{{ playerState?.ready || 'No player state' }}</p>
|
||||||
|
<p>Session started: {{ isSessionStarted }}</p>
|
||||||
</div>
|
</div>
|
||||||
<button class="button" @click="startGame">Start Game</button>
|
<div class="block">
|
||||||
|
<input class="input" style="margin-bottom: 0" v-model="seed" placeholder="Seed" />
|
||||||
|
</div>
|
||||||
|
<button class="button" @click="createMatch" v-if="!isSessionStarted">
|
||||||
|
Create Match Session
|
||||||
|
</button>
|
||||||
|
<button class="button" @click="setPlayerReady" v-if="isSessionStarted">
|
||||||
|
<span v-if="!readyForStart">Ready</span><span v-else>Unready</span>
|
||||||
|
</button>
|
||||||
|
<button class="button" @click="startMatch" v-if="readyForStart">
|
||||||
|
<span>Start</span>
|
||||||
|
</button>
|
||||||
</section>
|
</section>
|
||||||
<section class="section available-sessions">
|
<section class="section available-sessions">
|
||||||
<h2 class="title is-4">Available Sessions</h2>
|
<h2 class="title is-4">Available Sessions</h2>
|
||||||
|
@ -1,15 +1,24 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { AuthenticationService } from '@/services/AuthenticationService'
|
||||||
|
import { inject, ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const username = ref('')
|
const username = ref('')
|
||||||
const password = ref('')
|
const password = ref('')
|
||||||
|
|
||||||
function login() {
|
const authService = inject<AuthenticationService>('auth')
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
try {
|
||||||
|
await authService?.login(username.value, password.value)
|
||||||
|
router.push({ name: 'home' })
|
||||||
|
} catch (error) {
|
||||||
|
alert('Invalid username or password')
|
||||||
|
}
|
||||||
// if (username.value === 'admin' && password.value === 'password') {
|
// if (username.value === 'admin' && password.value === 'password') {
|
||||||
localStorage.setItem('token', 'true')
|
// localStorage.setItem('token', 'true')
|
||||||
router.push({ name: 'home' })
|
// router.push({ name: 'home' })
|
||||||
// } else {
|
// } else {
|
||||||
// alert('Invalid username or password')
|
// alert('Invalid username or password')
|
||||||
// }
|
// }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user