reduce size of game json files

This commit is contained in:
Zutatensuppe 2020-12-05 19:45:34 +01:00
parent c6899a615b
commit b6a3cfd8ba
9 changed files with 380 additions and 149 deletions

View file

@ -7,19 +7,19 @@ function exists(gameId) {
return (!!GAMES[gameId]) || false return (!!GAMES[gameId]) || false
} }
function setGame(gameId, game) { function createGame(id, puzzle, players, sockets, evtInfos) {
GAMES[gameId] = game return {
id: id,
puzzle: puzzle,
players: players,
sockets: sockets,
evtInfos: evtInfos,
}
} }
function playerExists(gameId, playerId) { function createPlayer(id, ts) {
return !!GAMES[gameId].players[playerId] return {
} id: id,
function addPlayer(gameId, playerId) {
const ts = Util.timestamp()
if (!GAMES[gameId].players[playerId]) {
GAMES[gameId].players[playerId] = {
id: playerId,
x: 0, x: 0,
y: 0, y: 0,
d: 0, // mouse down d: 0, // mouse down
@ -27,8 +27,50 @@ function addPlayer(gameId, playerId) {
color: '#ffffff', color: '#ffffff',
bgcolor: '#222222', bgcolor: '#222222',
points: 0, points: 0,
ts, ts: ts,
} }
}
function newGame({id, puzzle, players, sockets, evtInfos}) {
const game = createGame(id, puzzle, players, sockets, evtInfos)
setGame(id, game)
return game
}
function setGame(gameId, game) {
GAMES[gameId] = game
}
function getPlayer(gameId, playerId) {
return Util.decodePlayer(GAMES[gameId].players[playerId])
}
function setPlayer(gameId, playerId, player) {
GAMES[gameId].players[playerId] = Util.encodePlayer(player)
}
function setTile(gameId, tileIdx, tile) {
GAMES[gameId].puzzle.tiles[tileIdx] = Util.encodeTile(tile)
}
function setPuzzleData(gameId, data) {
GAMES[gameId].puzzle.data = data
}
function playerExists(gameId, playerId) {
return !!GAMES[gameId].players[playerId]
}
function getActivePlayers(gameId) {
const ts = Util.timestamp()
const minTs = ts - 30000
return getAllPlayers(gameId).filter(player => player.ts >= minTs)
}
function addPlayer(gameId, playerId) {
const ts = Util.timestamp()
if (!GAMES[gameId].players[playerId]) {
setPlayer(gameId, playerId, createPlayer(playerId, ts))
} else { } else {
changePlayer(gameId, playerId, { ts }) changePlayer(gameId, playerId, { ts })
} }
@ -56,21 +98,52 @@ function removeSocket(gameId, socket) {
} }
function getAllGames() { function getAllGames() {
return GAMES return Object.values(GAMES)
}
function getAllPlayers(gameId) {
return GAMES[gameId]
? Object.values(GAMES[gameId].players).map(Util.decodePlayer)
: []
} }
function get(gameId) { function get(gameId) {
return GAMES[gameId] return GAMES[gameId]
} }
function getTileCount(gameId) {
return GAMES[gameId].puzzle.tiles.length
}
function getImageUrl(gameId) {
return GAMES[gameId].puzzle.info.imageUrl
}
function getFinishedTileCount(gameId) {
let count = 0
for (let t of GAMES[gameId].puzzle.tiles) {
if (Util.decodeTile(t).owner === -1) {
count++
}
}
return count
}
function getTilesSortedByZIndex(gameId) {
const tiles = GAMES[gameId].puzzle.tiles.map(Util.decodeTile)
return tiles.sort((t1, t2) => t1.z - t2.z)
}
function getSockets(gameId) { function getSockets(gameId) {
return GAMES[gameId].sockets return GAMES[gameId].sockets
} }
function changePlayer(gameId, playerId, change) { function changePlayer(gameId, playerId, change) {
const player = getPlayer(gameId, playerId)
for (let k of Object.keys(change)) { for (let k of Object.keys(change)) {
GAMES[gameId].players[playerId][k] = change[k] player[k] = change[k]
} }
setPlayer(gameId, playerId, player)
} }
function changeData(gameId, change) { function changeData(gameId, change) {
@ -81,12 +154,14 @@ function changeData(gameId, change) {
function changeTile(gameId, tileIdx, change) { function changeTile(gameId, tileIdx, change) {
for (let k of Object.keys(change)) { for (let k of Object.keys(change)) {
GAMES[gameId].puzzle.tiles[tileIdx][k] = change[k] const tile = Util.decodeTile(GAMES[gameId].puzzle.tiles[tileIdx])
tile[k] = change[k]
GAMES[gameId].puzzle.tiles[tileIdx] = Util.encodeTile(tile)
} }
} }
const getTile = (gameId, tileIdx) => { const getTile = (gameId, tileIdx) => {
return GAMES[gameId].puzzle.tiles[tileIdx] return Util.decodeTile(GAMES[gameId].puzzle.tiles[tileIdx])
} }
const getTileGroup = (gameId, tileIdx) => { const getTileGroup = (gameId, tileIdx) => {
@ -116,13 +191,27 @@ const getTileZIndex = (gameId, tileIdx) => {
const getFirstOwnedTileIdx = (gameId, userId) => { const getFirstOwnedTileIdx = (gameId, userId) => {
for (let t of GAMES[gameId].puzzle.tiles) { for (let t of GAMES[gameId].puzzle.tiles) {
if (t.owner === userId) { const tile = Util.decodeTile(t)
return t.idx if (tile.owner === userId) {
return tile.idx
} }
} }
return -1 return -1
} }
const getFirstOwnedTile = (gameId, userId) => {
const idx = getFirstOwnedTileIdx(gameId, userId)
return idx < 0 ? null : GAMES[gameId].puzzle.tiles[idx]
}
const getTileDrawOffset = (gameId) => {
return GAMES[gameId].puzzle.info.tileDrawOffset
}
const getTileDrawSize = (gameId) => {
return GAMES[gameId].puzzle.info.tileDrawSize
}
const getMaxGroup = (gameId) => { const getMaxGroup = (gameId) => {
return GAMES[gameId].puzzle.data.maxGroup return GAMES[gameId].puzzle.data.maxGroup
} }
@ -145,7 +234,7 @@ const getMaxZIndexByTileIdxs = (gameId, tileIdxs) => {
function srcPosByTileIdx(gameId, tileIdx) { function srcPosByTileIdx(gameId, tileIdx) {
const info = GAMES[gameId].puzzle.info const info = GAMES[gameId].puzzle.info
const c = info.coords[tileIdx] const c = Util.coordByTileIdx(info, tileIdx)
const cx = c.x * info.tileSize const cx = c.x * info.tileSize
const cy = c.y * info.tileSize const cy = c.y * info.tileSize
@ -155,18 +244,17 @@ function srcPosByTileIdx(gameId, tileIdx) {
function getSurroundingTilesByIdx(gameId, tileIdx) { function getSurroundingTilesByIdx(gameId, tileIdx) {
const info = GAMES[gameId].puzzle.info const info = GAMES[gameId].puzzle.info
const _X = info.coords[tileIdx].x const c = Util.coordByTileIdx(info, tileIdx)
const _Y = info.coords[tileIdx].y
return [ return [
// top // top
(_Y > 0) ? (tileIdx - info.tilesX) : -1, (c.y > 0) ? (tileIdx - info.tilesX) : -1,
// right // right
(_X < info.tilesX - 1) ? (tileIdx + 1) : -1, (c.x < info.tilesX - 1) ? (tileIdx + 1) : -1,
// bottom // bottom
(_Y < info.tilesY - 1) ? (tileIdx + info.tilesX) : -1, (c.y < info.tilesY - 1) ? (tileIdx + info.tilesX) : -1,
// left // left
(_X > 0) ? (tileIdx - 1) : -1, (c.x > 0) ? (tileIdx - 1) : -1,
] ]
} }
@ -203,13 +291,14 @@ const setTilesOwner = (gameId, tileIdxs, owner) => {
// get all grouped tiles for a tile // get all grouped tiles for a tile
function getGroupedTileIdxs(gameId, tileIdx) { function getGroupedTileIdxs(gameId, tileIdx) {
const tiles = GAMES[gameId].puzzle.tiles const tiles = GAMES[gameId].puzzle.tiles
const tile = tiles[tileIdx] const tile = Util.decodeTile(tiles[tileIdx])
const grouped = [] const grouped = []
if (tile.group) { if (tile.group) {
for (let other of tiles) { for (let other of tiles) {
if (other.group === tile.group) { const otherTile = Util.decodeTile(other)
grouped.push(other.idx) if (otherTile.group === tile.group) {
grouped.push(otherTile.idx)
} }
} }
} else { } else {
@ -227,7 +316,7 @@ const freeTileIdxByPos = (gameId, pos) => {
let maxZ = -1 let maxZ = -1
let tileIdx = -1 let tileIdx = -1
for (let idx = 0; idx < tiles.length; idx++) { for (let idx = 0; idx < tiles.length; idx++) {
const tile = tiles[idx] const tile = Util.decodeTile(tiles[idx])
if (tile.owner !== 0) { if (tile.owner !== 0) {
continue continue
} }
@ -248,6 +337,22 @@ const freeTileIdxByPos = (gameId, pos) => {
return tileIdx return tileIdx
} }
const getPlayerBgColor = (gameId, playerId) => {
return getPlayer(gameId, playerId).bgcolor
}
const getPlayerColor = (gameId, playerId) => {
return getPlayer(gameId, playerId).color
}
const getPlayerName = (gameId, playerId) => {
return getPlayer(gameId, playerId).name
}
const getPlayerPoints = (gameId, playerId) => {
return getPlayer(gameId, playerId).points
}
// determine if two tiles are grouped together // determine if two tiles are grouped together
const areGrouped = (gameId, tileIdx1, tileIdx2) => { const areGrouped = (gameId, tileIdx1, tileIdx2) => {
const g1 = getTileGroup(gameId, tileIdx1) const g1 = getTileGroup(gameId, tileIdx1)
@ -255,9 +360,24 @@ const areGrouped = (gameId, tileIdx1, tileIdx2) => {
return g1 && g1 === g2 return g1 && g1 === g2
} }
const getTableWidth = (gameId) => {
return GAMES[gameId].puzzle.info.table.width
}
const getTableHeight = (gameId) => {
return GAMES[gameId].puzzle.info.table.height
}
const getPuzzleWidth = (gameId) => {
return GAMES[gameId].puzzle.info.width
}
const getPuzzleHeight = (gameId) => {
return GAMES[gameId].puzzle.info.height
}
function handleInput(gameId, playerId, input) { function handleInput(gameId, playerId, input) {
let puzzle = GAMES[gameId].puzzle const puzzle = GAMES[gameId].puzzle
let players = GAMES[gameId].players
let evtInfo = GAMES[gameId].evtInfos[playerId] let evtInfo = GAMES[gameId].evtInfos[playerId]
let changes = [] let changes = []
@ -277,7 +397,7 @@ function handleInput(gameId, playerId, input) {
} }
const _playerChange = () => { const _playerChange = () => {
changes.push(['player', players[playerId]]) changes.push(['player', GAMES[gameId].players[playerId]])
} }
// put both tiles (and their grouped tiles) in the same group // put both tiles (and their grouped tiles) in the same group
@ -312,7 +432,8 @@ function handleInput(gameId, playerId, input) {
// TODO: strange // TODO: strange
if (searchGroups.length > 0) { if (searchGroups.length > 0) {
for (let tile of tiles) { for (let t of tiles) {
const tile = Util.decodeTile(t)
if (searchGroups.includes(tile.group)) { if (searchGroups.includes(tile.group)) {
changeTile(gameId, tile.idx, { group }) changeTile(gameId, tile.idx, { group })
_tileChange(tile.idx) _tileChange(tile.idx)
@ -404,7 +525,7 @@ function handleInput(gameId, playerId, input) {
// Snap the tile to the final destination // Snap the tile to the final destination
moveTilesDiff(gameId, tileIdxs, diff) moveTilesDiff(gameId, tileIdxs, diff)
finishTiles(gameId, tileIdxs) finishTiles(gameId, tileIdxs)
changePlayer(gameId, playerId, { points: players[playerId].points + tileIdxs.length }) changePlayer(gameId, playerId, { points: getPlayerPoints(gameId, playerId) + tileIdxs.length })
_tileChanges(tileIdxs) _tileChanges(tileIdxs)
} else { } else {
// Snap to other tiles // Snap to other tiles
@ -457,17 +578,35 @@ function handleInput(gameId, playerId, input) {
return changes return changes
} }
export default { export default {
setGame, newGame,
exists, exists,
playerExists, playerExists,
getActivePlayers,
addPlayer, addPlayer,
socketExists, socketExists,
addSocket, addSocket,
removeSocket, removeSocket,
getFinishedTileCount,
getTileCount,
getImageUrl,
get, get,
getAllGames, getAllGames,
getSockets, getSockets,
getPlayerBgColor,
getPlayerColor,
getPlayerName,
changePlayer,
setPlayer,
setTile,
setPuzzleData,
getTableWidth,
getTableHeight,
getPuzzleWidth,
getPuzzleHeight,
getTilesSortedByZIndex,
getFirstOwnedTile,
getTileDrawOffset,
getTileDrawSize,
handleInput, handleInput,
} }

View file

@ -46,6 +46,100 @@ export const timestamp = () => {
) )
} }
function encodeShape(data) {
if (typeof data === 'number') {
return data
}
/* encoded in 1 byte:
00000000
^^ top
^^ right
^^ bottom
^^ left
*/
return ((data.top + 1) << 0)
| ((data.right + 1) << 2)
| ((data.bottom + 1) << 4)
| ((data.left + 1) << 6)
}
function decodeShape(data) {
if (typeof data !== 'number') {
return data
}
return {
top: (data >> 0 & 0b11) - 1,
right: (data >> 2 & 0b11) - 1,
bottom: (data >> 4 & 0b11) - 1,
left: (data >> 6 & 0b11) - 1,
}
}
function encodeTile(data) {
if (Array.isArray(data)) {
return data
}
return [data.idx, data.pos.x, data.pos.y, data.z, data.owner, data.group]
}
function decodeTile(data) {
if (!Array.isArray(data)) {
return data
}
return {
idx: data[0],
pos: {
x: data[1],
y: data[2],
},
z: data[3],
owner: data[4],
group: data[5],
}
}
function encodePlayer(data) {
if (Array.isArray(data)) {
return data
}
return [
data.id,
data.x,
data.y,
data.d,
data.name,
data.color,
data.bgcolor,
data.points,
data.ts,
]
}
function decodePlayer(data) {
if (!Array.isArray(data)) {
return data
}
return {
id: data[0],
x: data[1],
y: data[2],
d: data[3], // mouse down
name: data[4],
color: data[5],
bgcolor: data[6],
points: data[7],
ts: data[8],
}
}
function coordByTileIdx(info, tileIdx) {
const wTiles = info.width / info.tileSize
return {
x: tileIdx % wTiles,
y: Math.floor(tileIdx / wTiles),
}
}
export default { export default {
uniqId, uniqId,
randomInt, randomInt,
@ -53,4 +147,15 @@ export default {
throttle, throttle,
shuffle, shuffle,
timestamp, timestamp,
encodeShape,
decodeShape,
encodeTile,
decodeTile,
encodePlayer,
decodePlayer,
coordByTileIdx,
} }

View file

@ -1,6 +1,23 @@
import GameCommon from './../common/GameCommon.js' import GameCommon from './../common/GameCommon.js'
export default { export default {
createGame: GameCommon.setGame, newGame: GameCommon.newGame,
getActivePlayers: GameCommon.getActivePlayers,
handleInput: GameCommon.handleInput, handleInput: GameCommon.handleInput,
getPlayerBgColor: GameCommon.getPlayerBgColor,
getPlayerColor: GameCommon.getPlayerColor,
getPlayerName: GameCommon.getPlayerName,
changePlayer: GameCommon.changePlayer,
setPlayer: GameCommon.setPlayer,
setTile: GameCommon.setTile,
getImageUrl: GameCommon.getImageUrl,
setPuzzleData: GameCommon.setPuzzleData,
getTableWidth: GameCommon.getTableWidth,
getTableHeight: GameCommon.getTableHeight,
getPuzzleWidth: GameCommon.getPuzzleWidth,
getPuzzleHeight: GameCommon.getPuzzleHeight,
getTilesSortedByZIndex: GameCommon.getTilesSortedByZIndex,
getFirstOwnedTile: GameCommon.getFirstOwnedTile,
getTileDrawOffset: GameCommon.getTileDrawOffset,
getTileDrawSize: GameCommon.getTileDrawSize,
} }

View file

@ -1,5 +1,6 @@
import Geometry from '../common/Geometry.js' import Geometry from '../common/Geometry.js'
import Graphics from './Graphics.js' import Graphics from './Graphics.js'
import Util from './../common/Util.js'
async function createPuzzleTileBitmaps(img, tiles, info) { async function createPuzzleTileBitmaps(img, tiles, info) {
var tileSize = info.tileSize var tileSize = info.tileSize
@ -76,9 +77,10 @@ async function createPuzzleTileBitmaps(img, tiles, info) {
return path return path
} }
for (let tile of tiles) { for (let t of tiles) {
const tile = Util.decodeTile(t)
const srcRect = srcRectByIdx(info, tile.idx) const srcRect = srcRectByIdx(info, tile.idx)
const path = pathForShape(info.shapes[tile.idx]) const path = pathForShape(Util.decodeShape(info.shapes[tile.idx]))
const c = Graphics.createCanvas(tileDrawSize, tileDrawSize) const c = Graphics.createCanvas(tileDrawSize, tileDrawSize)
const ctx = c.getContext('2d') const ctx = c.getContext('2d')
@ -192,7 +194,7 @@ async function createPuzzleTileBitmaps(img, tiles, info) {
} }
function srcRectByIdx(puzzleInfo, idx) { function srcRectByIdx(puzzleInfo, idx) {
const c = puzzleInfo.coords[idx] const c = Util.coordByTileIdx(puzzleInfo, idx)
return { return {
x: c.x * puzzleInfo.tileSize, x: c.x * puzzleInfo.tileSize,
y: c.y * puzzleInfo.tileSize, y: c.y * puzzleInfo.tileSize,

View file

@ -27,18 +27,6 @@ function addCanvasToDom(canvas) {
return canvas return canvas
} }
function getActivePlayers(players) {
const ts = Util.timestamp()
const activePlayers = []
for (let id of Object.keys(players)) {
const player = players[id]
if (player.ts >= ts - 30000) {
activePlayers.push(player)
}
}
return activePlayers
}
function addMenuToDom(previewImageUrl) { function addMenuToDom(previewImageUrl) {
function row (...elements) { function row (...elements) {
const row = document.createElement('tr') const row = document.createElement('tr')
@ -141,8 +129,8 @@ function addMenuToDom(previewImageUrl) {
scoresTitleEl.appendChild(document.createTextNode('Scores')) scoresTitleEl.appendChild(document.createTextNode('Scores'))
const scoresListEl = document.createElement('table') const scoresListEl = document.createElement('table')
const updateScores = (players) => { const updateScores = (gameId) => {
const activePlayers = getActivePlayers(players) const activePlayers = Game.getActivePlayers(gameId)
const scores = activePlayers.map(p => ({ const scores = activePlayers.map(p => ({
name: p.name, name: p.name,
points: p.points, points: p.points,
@ -188,15 +176,6 @@ function initme() {
return ID return ID
} }
const getFirstOwnedTile = (puzzle, userId) => {
for (let t of puzzle.tiles) {
if (t.owner === userId) {
return t
}
}
return null
}
export default class EventAdapter { export default class EventAdapter {
constructor(canvas, viewport) { constructor(canvas, viewport) {
this.events = [] this.events = []
@ -279,20 +258,12 @@ async function main() {
} }
const game = await Communication.connect(gameId, CLIENT_ID) const game = await Communication.connect(gameId, CLIENT_ID)
Game.createGame(GAME_ID, game); Game.newGame(game)
const bitmaps = await PuzzleGraphics.loadPuzzleBitmaps(game.puzzle) const bitmaps = await PuzzleGraphics.loadPuzzleBitmaps(game.puzzle)
const puzzle = game.puzzle
const players = game.players
const changePlayer = (change) => { const {bgColorPickerEl, playerColorPickerEl, nameChangeEl, updateScores} = addMenuToDom(Game.getImageUrl(gameId))
for (let k of Object.keys(change)) { updateScores(gameId)
players[CLIENT_ID][k] = change[k]
}
}
const {bgColorPickerEl, playerColorPickerEl, nameChangeEl, updateScores} = addMenuToDom(game.puzzle.info.imageUrl)
updateScores(players)
// Create a dom and attach adapters to it so we can work with it // Create a dom and attach adapters to it so we can work with it
const canvas = addCanvasToDom(Graphics.createCanvas()) const canvas = addCanvasToDom(Graphics.createCanvas())
@ -303,21 +274,21 @@ async function main() {
const viewport = new Camera(canvas) const viewport = new Camera(canvas)
// center viewport // center viewport
viewport.move( viewport.move(
-(puzzle.info.table.width - viewport.width) /2, -(Game.getTableWidth(gameId) - viewport.width) /2,
-(puzzle.info.table.height - viewport.height) /2 -(Game.getTableHeight(gameId) - viewport.height) /2
) )
const evts = new EventAdapter(canvas, viewport) const evts = new EventAdapter(canvas, viewport)
bgColorPickerEl.value = players[CLIENT_ID].bgcolor bgColorPickerEl.value = Game.getPlayerBgColor(gameId, CLIENT_ID)
bgColorPickerEl.addEventListener('change', () => { bgColorPickerEl.addEventListener('change', () => {
evts.addEvent(['bg_color', bgColorPickerEl.value]) evts.addEvent(['bg_color', bgColorPickerEl.value])
}) })
playerColorPickerEl.value = players[CLIENT_ID].color playerColorPickerEl.value = Game.getPlayerBgColor(gameId, CLIENT_ID)
playerColorPickerEl.addEventListener('change', () => { playerColorPickerEl.addEventListener('change', () => {
evts.addEvent(['player_color', playerColorPickerEl.value]) evts.addEvent(['player_color', playerColorPickerEl.value])
}) })
nameChangeEl.value = players[CLIENT_ID].name nameChangeEl.value = Game.getPlayerName(gameId, CLIENT_ID)
nameChangeEl.addEventListener('change', () => { nameChangeEl.addEventListener('change', () => {
evts.addEvent(['player_name', nameChangeEl.value]) evts.addEvent(['player_name', nameChangeEl.value])
}) })
@ -330,28 +301,25 @@ async function main() {
for(let [changeType, changeData] of evChanges) { for(let [changeType, changeData] of evChanges) {
switch (changeType) { switch (changeType) {
case 'player': { case 'player': {
if (changeData.id !== CLIENT_ID) { const p = Util.decodePlayer(changeData)
players[changeData.id] = changeData if (p.id !== CLIENT_ID) {
Game.setPlayer(gameId, p.id, p)
RERENDER = true RERENDER = true
} }
} break; } break;
case 'tile': { case 'tile': {
puzzle.tiles[changeData.idx] = changeData const t = Util.decodeTile(changeData)
Game.setTile(gameId, t.idx, t)
RERENDER = true RERENDER = true
} break; } break;
case 'data': { case 'data': {
puzzle.data = changeData Game.setPuzzleData(gameId, changeData)
RERENDER = true RERENDER = true
} break; } break;
} }
} }
}) })
const tilesSortedByZIndex = () => {
const sorted = puzzle.tiles.slice()
return sorted.sort((t1, t2) => t1.z - t2.z)
}
let _last_mouse_down = null let _last_mouse_down = null
const onUpdate = () => { const onUpdate = () => {
for (let evt of evts.consumeAll()) { for (let evt of evts.consumeAll()) {
@ -360,12 +328,9 @@ async function main() {
// ------------------------------------------------------------- // -------------------------------------------------------------
const type = evt[0] const type = evt[0]
if (type === 'move') { if (type === 'move') {
const pos = { x: evt[1], y: evt[2] } if (_last_mouse_down && !Game.getFirstOwnedTile(gameId, CLIENT_ID)) {
RERENDER = true
changePlayer(pos)
if (_last_mouse_down && !getFirstOwnedTile(puzzle, CLIENT_ID)) {
// move the cam // move the cam
const pos = { x: evt[1], y: evt[2] }
const mouse = viewport.worldToViewport(pos) const mouse = viewport.worldToViewport(pos)
const diffX = Math.round(mouse.x - _last_mouse_down.x) const diffX = Math.round(mouse.x - _last_mouse_down.x)
const diffY = Math.round(mouse.y - _last_mouse_down.y) const diffY = Math.round(mouse.y - _last_mouse_down.y)
@ -382,13 +347,13 @@ async function main() {
if (viewport.zoomIn()) { if (viewport.zoomIn()) {
const pos = { x: evt[1], y: evt[2] } const pos = { x: evt[1], y: evt[2] }
RERENDER = true RERENDER = true
changePlayer(pos) Game.changePlayer(gameId, CLIENT_ID, pos)
} }
} else if (type === 'zoomout') { } else if (type === 'zoomout') {
if (viewport.zoomOut()) { if (viewport.zoomOut()) {
const pos = { x: evt[1], y: evt[2] } const pos = { x: evt[1], y: evt[2] }
RERENDER = true RERENDER = true
changePlayer(pos) Game.changePlayer(gameId, CLIENT_ID, pos)
} }
} }
@ -414,7 +379,7 @@ async function main() {
// CLEAR CTX // CLEAR CTX
// --------------------------------------------------------------- // ---------------------------------------------------------------
ctx.fillStyle = players[CLIENT_ID].bgcolor || '#222222' ctx.fillStyle = Game.getPlayerBgColor(gameId, CLIENT_ID) || '#222222'
ctx.fillRect(0, 0, canvas.width, canvas.height) ctx.fillRect(0, 0, canvas.width, canvas.height)
if (DEBUG) Debug.checkpoint('clear done') if (DEBUG) Debug.checkpoint('clear done')
// --------------------------------------------------------------- // ---------------------------------------------------------------
@ -423,12 +388,12 @@ async function main() {
// DRAW BOARD // DRAW BOARD
// --------------------------------------------------------------- // ---------------------------------------------------------------
pos = viewport.worldToViewport({ pos = viewport.worldToViewport({
x: (puzzle.info.table.width - puzzle.info.width) / 2, x: (Game.getTableWidth(gameId) - Game.getPuzzleWidth(gameId)) / 2,
y: (puzzle.info.table.height - puzzle.info.height) / 2 y: (Game.getTableHeight(gameId) - Game.getPuzzleHeight(gameId)) / 2
}) })
dim = viewport.worldDimToViewport({ dim = viewport.worldDimToViewport({
w: puzzle.info.width, w: Game.getPuzzleWidth(gameId),
h: puzzle.info.height, h: Game.getPuzzleHeight(gameId),
}) })
ctx.fillStyle = 'rgba(255, 255, 255, .5)' ctx.fillStyle = 'rgba(255, 255, 255, .5)'
ctx.fillRect(pos.x, pos.y, dim.w, dim.h) ctx.fillRect(pos.x, pos.y, dim.w, dim.h)
@ -438,15 +403,15 @@ async function main() {
// DRAW TILES // DRAW TILES
// --------------------------------------------------------------- // ---------------------------------------------------------------
for (let tile of tilesSortedByZIndex()) { for (let tile of Game.getTilesSortedByZIndex(gameId)) {
const bmp = bitmaps[tile.idx] const bmp = bitmaps[tile.idx]
pos = viewport.worldToViewport({ pos = viewport.worldToViewport({
x: puzzle.info.tileDrawOffset + tile.pos.x, x: Game.getTileDrawOffset(gameId) + tile.pos.x,
y: puzzle.info.tileDrawOffset + tile.pos.y, y: Game.getTileDrawOffset(gameId) + tile.pos.y,
}) })
dim = viewport.worldDimToViewport({ dim = viewport.worldDimToViewport({
w: puzzle.info.tileDrawSize, w: Game.getTileDrawSize(gameId),
h: puzzle.info.tileDrawSize, h: Game.getTileDrawSize(gameId),
}) })
ctx.drawImage(bmp, ctx.drawImage(bmp,
0, 0, bmp.width, bmp.height, 0, 0, bmp.width, bmp.height,
@ -459,18 +424,18 @@ async function main() {
// DRAW PLAYERS // DRAW PLAYERS
// --------------------------------------------------------------- // ---------------------------------------------------------------
for (let p of getActivePlayers(players)) { for (let player of Game.getActivePlayers(gameId)) {
const cursor = await getPlayerCursor(p) const cursor = await getPlayerCursor(player)
const pos = viewport.worldToViewport(p) const pos = viewport.worldToViewport(player)
ctx.drawImage(cursor, ctx.drawImage(cursor,
Math.round(pos.x - cursor.width/2), Math.round(pos.x - cursor.width/2),
Math.round(pos.y - cursor.height/2) Math.round(pos.y - cursor.height/2)
) )
if (p.id !== CLIENT_ID) { if (player.id !== CLIENT_ID) {
ctx.fillStyle = 'white' ctx.fillStyle = 'white'
ctx.font = '10px sans-serif' ctx.font = '10px sans-serif'
ctx.textAlign = 'center' ctx.textAlign = 'center'
ctx.fillText(p.name + ' (' + p.points + ')', ctx.fillText(player.name + ' (' + player.points + ')',
Math.round(pos.x), Math.round(pos.x),
Math.round(pos.y) + cursor.height Math.round(pos.y) + cursor.height
) )
@ -480,7 +445,7 @@ async function main() {
// DRAW PLAYERS // DRAW PLAYERS
// --------------------------------------------------------------- // ---------------------------------------------------------------
updateScores(players) updateScores(gameId)
if (DEBUG) Debug.checkpoint('scores done') if (DEBUG) Debug.checkpoint('scores done')
// --------------------------------------------------------------- // ---------------------------------------------------------------

View file

@ -38,12 +38,16 @@ export default {
<div id="app"> <div id="app">
<h1>Running games</h1> <h1>Running games</h1>
<div v-for="g in games"> <div v-for="g in games">
<a :href="'/g/' + g.id">{{g.title}}</a> <a :href="'/g/' + g.id">
<img :src="g.imageUrl" style="width: 150px; display: inline-block; margin: 5px;" />
<br />
{{g.tilesFinished}}/{{g.tilesTotal}} pieces, {{g.players}} players
</a>
</div> </div>
<h1>New game</h1> <h1>New game</h1>
<div> <div>
<label>Tiles: </label> <label>Pieces: </label>
<input type="text" v-model="tiles" /> <input type="text" v-model="tiles" />
</div> </div>
<div> <div>

View file

@ -3,13 +3,13 @@ import { createPuzzle } from './Puzzle.js'
import GameCommon from './../common/GameCommon.js' import GameCommon from './../common/GameCommon.js'
async function createGame(gameId, targetTiles, image) { async function createGame(gameId, targetTiles, image) {
const game = { GameCommon.newGame({
id: gameId,
puzzle: await createPuzzle(targetTiles, image), puzzle: await createPuzzle(targetTiles, image),
players: {}, players: {},
sockets: [], sockets: [],
evtInfos: {}, evtInfos: {},
} })
GameCommon.setGame(gameId, game)
} }
function loadAllGames() { function loadAllGames() {
@ -21,20 +21,20 @@ function loadAllGames() {
const gameId = f.replace(/\.json$/, '') const gameId = f.replace(/\.json$/, '')
const contents = fs.readFileSync(`./../data/${f}`, 'utf-8') const contents = fs.readFileSync(`./../data/${f}`, 'utf-8')
const game = JSON.parse(contents) const game = JSON.parse(contents)
GameCommon.setGame(gameId, { GameCommon.newGame({
id: gameId,
puzzle: game.puzzle, puzzle: game.puzzle,
players: game.players, players: game.players,
sockets: [], sockets: [],
evtInfos: {}, evtInfos: {}
}) })
} }
} }
function persistAll() { function persistAll() {
const games = GameCommon.getAllGames() for (const game of GameCommon.getAllGames()) {
for (const gameId of Object.keys(games)) { fs.writeFileSync('./../data/' + game.id + '.json', JSON.stringify({
const game = games[gameId] id: game.id,
fs.writeFileSync('./../data/' + gameId + '.json', JSON.stringify({
puzzle: game.puzzle, puzzle: game.puzzle,
players: game.players, players: game.players,
})) }))
@ -43,9 +43,13 @@ function persistAll() {
export default { export default {
loadAllGames, loadAllGames,
getAllGames: GameCommon.getAllGames,
persistAll, persistAll,
createGame, createGame,
getAllGames: GameCommon.getAllGames,
getActivePlayers: GameCommon.getActivePlayers,
getFinishedTileCount: GameCommon.getFinishedTileCount,
getImageUrl: GameCommon.getImageUrl,
getTileCount: GameCommon.getTileCount,
exists: GameCommon.exists, exists: GameCommon.exists,
addPlayer: GameCommon.addPlayer, addPlayer: GameCommon.addPlayer,
playerExists: GameCommon.playerExists, playerExists: GameCommon.playerExists,

View file

@ -23,11 +23,12 @@ async function createPuzzle(targetTiles, image) {
let positions = new Array(info.tiles) let positions = new Array(info.tiles)
for (let tile of tiles) { for (let tile of tiles) {
let coord = Util.coordByTileIdx(info, tile.idx)
positions[tile.idx] ={ positions[tile.idx] ={
// instead of info.tileSize, we use info.tileDrawSize // instead of info.tileSize, we use info.tileDrawSize
// to spread the tiles a bit // to spread the tiles a bit
x: (info.coords[tile.idx].x) * (info.tileSize * 1.5), x: coord.x * info.tileSize * 1.5,
y: (info.coords[tile.idx].y) * (info.tileSize * 1.5), y: coord.y * info.tileSize * 1.5,
} }
} }
@ -73,7 +74,7 @@ async function createPuzzle(targetTiles, image) {
positions = Util.shuffle(positions) positions = Util.shuffle(positions)
tiles = tiles.map(tile => { tiles = tiles.map(tile => {
return { return Util.encodeTile({
idx: tile.idx, // index of tile in the array idx: tile.idx, // index of tile in the array
group: 0, // if grouped with other tiles group: 0, // if grouped with other tiles
z: 0, // z index of the tile z: 0, // z index of the tile
@ -88,7 +89,7 @@ async function createPuzzle(targetTiles, image) {
// this position is the initial position only and is the // this position is the initial position only and is the
// value that changes when moving a tile // value that changes when moving a tile
pos: positions[tile.idx], pos: positions[tile.idx],
} })
}) })
// Complete puzzle object // Complete puzzle object
@ -125,7 +126,6 @@ async function createPuzzle(targetTiles, image) {
tiles: info.tiles, // the final number of tiles in the puzzle tiles: info.tiles, // the final number of tiles in the puzzle
tilesX: info.tilesX, // number of tiles each row tilesX: info.tilesX, // number of tiles each row
tilesY: info.tilesY, // number of tiles each col tilesY: info.tilesY, // number of tiles each col
coords: info.coords, // map of tile index to its coordinates
// ( index => {x, y} ) // ( index => {x, y} )
// this is not the physical coordinate, but // this is not the physical coordinate, but
// the tile_coordinate // the tile_coordinate
@ -141,12 +141,13 @@ function determinePuzzleTileShapes(info) {
const shapes = new Array(info.tiles) const shapes = new Array(info.tiles)
for (let i = 0; i < info.tiles; i++) { for (let i = 0; i < info.tiles; i++) {
shapes[i] = { let coord = Util.coordByTileIdx(info, i)
top: info.coords[i].y === 0 ? 0 : shapes[i - info.tilesX].bottom * -1, shapes[i] = Util.encodeShape({
right: info.coords[i].x === info.tilesX - 1 ? 0 : Util.choice(tabs), top: coord.y === 0 ? 0 : shapes[i - info.tilesX].bottom * -1,
left: info.coords[i].x === 0 ? 0 : shapes[i - 1].right * -1, right: coord.x === info.tilesX - 1 ? 0 : Util.choice(tabs),
bottom: info.coords[i].y === info.tilesY - 1 ? 0 : Util.choice(tabs), left: coord.x === 0 ? 0 : shapes[i - 1].right * -1,
} bottom: coord.y === info.tilesY - 1 ? 0 : Util.choice(tabs),
})
} }
return shapes return shapes
} }
@ -173,7 +174,6 @@ const determinePuzzleInfo = (w, h, targetTiles) => {
const tileSize = TILE_SIZE const tileSize = TILE_SIZE
const width = tilesX * tileSize const width = tilesX * tileSize
const height = tilesY * tileSize const height = tilesY * tileSize
const coords = buildCoords({ width, height, tileSize, tiles })
const tileMarginWidth = tileSize * .5; const tileMarginWidth = tileSize * .5;
const tileDrawSize = Math.round(tileSize + tileMarginWidth * 2) const tileDrawSize = Math.round(tileSize + tileMarginWidth * 2)
@ -187,21 +187,9 @@ const determinePuzzleInfo = (w, h, targetTiles) => {
tiles, tiles,
tilesX, tilesX,
tilesY, tilesY,
coords,
} }
} }
const buildCoords = (puzzleInfo) => {
const wTiles = puzzleInfo.width / puzzleInfo.tileSize
const coords = new Array(puzzleInfo.tiles)
for (let i = 0; i < puzzleInfo.tiles; i++) {
const y = Math.floor(i / wTiles)
const x = i % wTiles
coords[i] = { x, y }
}
return coords
}
export { export {
createPuzzle, createPuzzle,
} }

View file

@ -76,9 +76,12 @@ app.use('/uploads/', express.static('./../data/uploads/'))
app.use('/', async (req, res, next) => { app.use('/', async (req, res, next) => {
if (req.path === '/') { if (req.path === '/') {
const games = [ const games = [
...Object.keys(Game.getAllGames()).map(id => ({ ...Game.getAllGames().map(game => ({
id: id, id: game.id,
title: id, tilesFinished: Game.getFinishedTileCount(game.id),
tilesTotal: Game.getTileCount(game.id),
players: Game.getActivePlayers(game.id).length,
imageUrl: Game.getImageUrl(game.id),
})), })),
] ]
@ -126,8 +129,10 @@ wss.on('message', async ({socket, data}) => {
const game = Game.get(gameId) const game = Game.get(gameId)
notify( notify(
[Protocol.EV_SERVER_INIT, { [Protocol.EV_SERVER_INIT, {
id: game.id,
puzzle: game.puzzle, puzzle: game.puzzle,
players: game.players, players: game.players,
sockets: [],
evtInfos: game.evtInfos, evtInfos: game.evtInfos,
}], }],
[socket] [socket]
@ -143,8 +148,10 @@ wss.on('message', async ({socket, data}) => {
const game = Game.get(gameId) const game = Game.get(gameId)
notify( notify(
[Protocol.EV_SERVER_INIT, { [Protocol.EV_SERVER_INIT, {
id: game.id,
puzzle: game.puzzle, puzzle: game.puzzle,
players: game.players, players: game.players,
sockets: [],
evtInfos: game.evtInfos, evtInfos: game.evtInfos,
}], }],
[socket] [socket]