Better error handling, birthdays, startup otbs, resource caching.

leave-game
Thomas Hintz 5 years ago
parent 197e4ffc5d
commit 333bd70d1a

@ -44,7 +44,7 @@ farm:
make src/server/farm make src/server/farm
runprod: runprod:
cd dist/ && chmod +x farm && ./farm cd dist/ && chmod +x farm && ./farm -:a50
upload: upload:
rsync -rtvz dist/ $(SERVER):~/farm rsync -rtvz dist/ $(SERVER):~/farm

@ -47,7 +47,7 @@
"20 Cows on Peridier Ridge")))) "20 Cows on Peridier Ridge"))))
(define *ff-text* (define *ff-text*
'(((p (img (@ (src "53c3a93b0867eee67b9b9f6ebc4c1f4a.gif") (style "float: left;"))) "Natural Disaster--The Solar Winds break through the atmosphere. You are luckily shielded by Mt Proctor. Your hay survives and jumps in price. " (b "COLLECT $500 per Hay acre") ". To see if they escaped, other players must roll. Odd: escaped, Even: hit. " (b "Wind hit players must clean up all acres at $100 per acre."))) '(((p (img (@ (src "./assets/img/volcano2.53c3a93b0867eee67b9b9f6ebc4c1f4a.gif") (style "float: left;"))) "Natural Disaster--The Solar Winds break through the atmosphere. You are luckily shielded by Mt Proctor. Your hay survives and jumps in price. " (b "COLLECT $500 per Hay acre") ". To see if they escaped, other players must roll. Odd: escaped, Even: hit. " (b "Wind hit players must clean up all acres at $100 per acre.")))
((p "Planetary Disaster Fund comes through." (p (b "COLLECT $100 per Grain acre.")))) ((p "Planetary Disaster Fund comes through." (p (b "COLLECT $100 per Grain acre."))))
((p "Another high wind spring and your wheat didn't get sprayed. Weeds take over and cut your harvest in half. Hold this card through Wheat Harvest for this year.")) ((p "Another high wind spring and your wheat didn't get sprayed. Weeds take over and cut your harvest in half. Hold this card through Wheat Harvest for this year."))
((p "Kept back some of your cows and Proxima B steak goes viral.") (p (b "COLLECT $2,000 if you have cows."))) ((p "Kept back some of your cows and Proxima B steak goes viral.") (p (b "COLLECT $2,000 if you have cows.")))

53
package-lock.json generated

@ -2212,8 +2212,7 @@
"array-uniq": { "array-uniq": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
"integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY="
"dev": true
}, },
"array-unique": { "array-unique": {
"version": "0.3.2", "version": "0.3.2",
@ -2667,8 +2666,7 @@
"big.js": { "big.js": {
"version": "5.2.2", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
"dev": true
}, },
"bignumber.js": { "bignumber.js": {
"version": "2.4.0", "version": "2.4.0",
@ -3787,6 +3785,16 @@
"integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=", "integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=",
"dev": true "dev": true
}, },
"css-url-relative-plugin": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/css-url-relative-plugin/-/css-url-relative-plugin-1.0.0.tgz",
"integrity": "sha1-T4FVU2I2Tw8ZG9HFKLwnsKdqFGw=",
"requires": {
"loader-utils": "^1.1.0",
"parse-import": "^2.0.0",
"webpack-sources": "^1.1.0"
}
},
"css-what": { "css-what": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
@ -4189,8 +4197,7 @@
"emojis-list": { "emojis-list": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k="
"dev": true
}, },
"end-of-stream": { "end-of-stream": {
"version": "1.4.4", "version": "1.4.4",
@ -5486,6 +5493,15 @@
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true "dev": true
}, },
"get-imports": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/get-imports/-/get-imports-1.0.0.tgz",
"integrity": "sha1-R8C07piTUWQsVJdxk79Pyqv1N48=",
"requires": {
"array-uniq": "^1.0.1",
"import-regex": "^1.1.0"
}
},
"get-stdin": { "get-stdin": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
@ -6018,6 +6034,11 @@
"resolve-cwd": "^2.0.0" "resolve-cwd": "^2.0.0"
} }
}, },
"import-regex": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/import-regex/-/import-regex-1.1.0.tgz",
"integrity": "sha1-pVxS5McFx2XKIQ6SQqBrvMiqf2Y="
},
"imurmurhash": { "imurmurhash": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@ -6500,7 +6521,6 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": { "requires": {
"minimist": "^1.2.0" "minimist": "^1.2.0"
} }
@ -6619,7 +6639,6 @@
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
"integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
"dev": true,
"requires": { "requires": {
"big.js": "^5.2.2", "big.js": "^5.2.2",
"emojis-list": "^2.0.0", "emojis-list": "^2.0.0",
@ -6918,8 +6937,7 @@
"minimist": { "minimist": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
"dev": true
}, },
"minipass": { "minipass": {
"version": "3.1.1", "version": "3.1.1",
@ -7681,6 +7699,14 @@
"integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==", "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==",
"dev": true "dev": true
}, },
"parse-import": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/parse-import/-/parse-import-2.0.0.tgz",
"integrity": "sha1-KyR0Aw4AirmNt2xLy/TbWucwb18=",
"requires": {
"get-imports": "^1.0.0"
}
},
"parse-json": { "parse-json": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
@ -9694,8 +9720,7 @@
"source-list-map": { "source-list-map": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
"integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw=="
"dev": true
}, },
"source-map": { "source-map": {
"version": "0.5.7", "version": "0.5.7",
@ -10797,7 +10822,6 @@
"version": "1.4.3", "version": "1.4.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
"integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
"dev": true,
"requires": { "requires": {
"source-list-map": "^2.0.0", "source-list-map": "^2.0.0",
"source-map": "~0.6.1" "source-map": "~0.6.1"
@ -10806,8 +10830,7 @@
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
"dev": true
} }
} }
}, },

@ -46,6 +46,7 @@
"@fortawesome/free-solid-svg-icons": "^5.12.0", "@fortawesome/free-solid-svg-icons": "^5.12.0",
"@fortawesome/react-fontawesome": "^0.1.8", "@fortawesome/react-fontawesome": "^0.1.8",
"cookies-js": "^1.2.3", "cookies-js": "^1.2.3",
"css-url-relative-plugin": "^1.0.0",
"mobx": "^5.15.3", "mobx": "^5.15.3",
"mobx-react": "^6.1.4", "mobx-react": "^6.1.4",
"react": "^16.12.0", "react": "^16.12.0",

@ -348,6 +348,7 @@ class PlayerSummary extends React.Component {
<PlayerTurnContainer player={player} <PlayerTurnContainer player={player}
game={this.props.game} game={this.props.game}
ui={this.props.ui} ui={this.props.ui}
hideForLarge={this.props.hideForLarge}
screen={this.props.screen} screen={this.props.screen}
showScreen={this.props.showScreen}/> showScreen={this.props.showScreen}/>
</GroupBox> </GroupBox>
@ -426,7 +427,9 @@ class PlayerTurnContainer extends React.Component {
<br /> <br />
</> </>
) : (<></>)} ) : (<></>)}
{view} <div className={this.props.hideForLarge ? 'hide-for-large' : ''}>
{view}
</div>
</div> </div>
</Col> </Col>
</Row> </Row>
@ -1885,7 +1888,7 @@ class Info extends React.Component {
const SCREENS = { summary: 'summary', misc: 'misc', farms: 'farms', const SCREENS = { summary: 'summary', misc: 'misc', farms: 'farms',
cards: 'cards', trade: 'trade', loans: 'loans', cards: 'cards', trade: 'trade', loans: 'loans',
action: 'action', info: 'info' }; action: 'action', info: 'info', error: 'error' };
class BoardApp extends React.Component { class BoardApp extends React.Component {
iconToScreen = { user: SCREENS.summary, 'window-restore': SCREENS.cards, iconToScreen = { user: SCREENS.summary, 'window-restore': SCREENS.cards,
@ -1948,6 +1951,10 @@ class BoardApp extends React.Component {
.find(p => p.player.name === this.props.game.currentPlayer).player; .find(p => p.player.name === this.props.game.currentPlayer).player;
this.setState({ currentPlayer }); this.setState({ currentPlayer });
} }
if (!prevProps.ui.exn && this.props.ui.exn) {
this.setState({ screen: SCREENS.error });
}
} }
componentDidMount() { componentDidMount() {
@ -2170,6 +2177,7 @@ class BoardApp extends React.Component {
<PlayerSummary player={this.props.player} <PlayerSummary player={this.props.player}
ui={this.props.ui} ui={this.props.ui}
screen={this.state.screen} screen={this.state.screen}
hideForLarge={true}
game={this.props.game} showScreen={this.showScreen} /> game={this.props.game} showScreen={this.showScreen} />
</div> </div>
<div className={this.tabClass(SCREENS.action)}> <div className={this.tabClass(SCREENS.action)}>
@ -2212,6 +2220,11 @@ class BoardApp extends React.Component {
<div className={this.tabClass(SCREENS.info)}> <div className={this.tabClass(SCREENS.info)}>
<Info harvestTable={this.props.ui.harvestTable} game={this.props.game} /> <Info harvestTable={this.props.ui.harvestTable} game={this.props.game} />
</div> </div>
<div className={this.tabClass(SCREENS.error)}>
<h3>Error</h3>
<p>A server error occured.</p>
<p><a href="/?reload-game=true">Reload game</a></p>
</div>
</div> </div>
<div className='static-tab-container show-for-large'> <div className='static-tab-container show-for-large'>
<div className='tab show'> <div className='tab show'>

@ -39,3 +39,4 @@ export const AUTO_SKIP = 'auto-skip'
export const MESSAGE = 'message' export const MESSAGE = 'message'
export const SET_HARVEST_TABLE = 'set-harvest-table' export const SET_HARVEST_TABLE = 'set-harvest-table'
export const SET_MOVING_SKIP = 'set-moving-skip' export const SET_MOVING_SKIP = 'set-moving-skip'
export const SERVER_ERROR = 'server-error'

@ -21,13 +21,14 @@ import { UPDATE_GAME, UPDATE_PLAYER, GAME_STATE, SET_SELECTED_CARD, SET_CARDS,
MP_MOUSE, SET_MP_DIMS, MARK_ACTION_CHANGE_HANDLED, SET_NEXT_ACTION, MP_MOUSE, SET_MP_DIMS, MARK_ACTION_CHANGE_HANDLED, SET_NEXT_ACTION,
MOVE_PLAYER, NEXT_UI_ACTION, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED, MOVE_PLAYER, NEXT_UI_ACTION, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED,
AUTO_SKIP, MESSAGE, SET_HARVEST_TABLE, SET_CARD_ERROR, AUTO_SKIP, MESSAGE, SET_HARVEST_TABLE, SET_CARD_ERROR,
SET_MOVING_SKIP } from './actionTypes.js' SET_MOVING_SKIP, SERVER_ERROR } from './actionTypes.js'
export { updateGame, updatePlayer, gameState, setSelectedCard, setCards, export { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
spacePushPlayer, spaceClearPlayers, setOldMessages, setMessagePanelSpace, spacePushPlayer, spaceClearPlayers, setOldMessages, setMessagePanelSpace,
mpMouse, setMPDims, movePlayer, setNextAction, nextUIAction, mpMouse, setMPDims, movePlayer, setNextAction, nextUIAction,
markActionChangeHandled, nextUIActionSilent, alert, alertHandled, markActionChangeHandled, nextUIActionSilent, alert, alertHandled,
autoSkip, message, setHarvestTable, setCardError, setMovingSkip } autoSkip, message, setHarvestTable, setCardError, setMovingSkip,
serverError }
function updateGame(update) { function updateGame(update) {
return { type: UPDATE_GAME, return { type: UPDATE_GAME,
@ -135,3 +136,7 @@ function setHarvestTable(table) {
function setMovingSkip(skip) { function setMovingSkip(skip) {
return { type: SET_MOVING_SKIP, skip }; return { type: SET_MOVING_SKIP, skip };
} }
function serverError(exn) {
return { type: SERVER_ERROR, exn };
}

@ -25,7 +25,7 @@ import { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
movePlayer, setOldMessages, markActionChangeHandled, movePlayer, setOldMessages, markActionChangeHandled,
mpMouse, rolled, setNextAction, nextUIAction, nextUIActionSilent, alert, mpMouse, rolled, setNextAction, nextUIAction, nextUIActionSilent, alert,
autoSkip, message, alertHandled, setHarvestTable, autoSkip, message, alertHandled, setHarvestTable,
setCardError, setMovingSkip } from './actions.js' setCardError, setMovingSkip, serverError } from './actions.js'
import { itemCard, fateCard } from 'game.js' import { itemCard, fateCard } from 'game.js'
export { initialize, buy, roll, endTurn, loan, trade, submitTradeAccept, export { initialize, buy, roll, endTurn, loan, trade, submitTradeAccept,
@ -42,6 +42,7 @@ function handleMessage(evt) {
if (data.event === 'error') { if (data.event === 'error') {
console.log('error:' + data.exn); console.log('error:' + data.exn);
store.dispatch(serverError(data.exn));
return; return;
} }
batch(() => { batch(() => {

@ -22,7 +22,7 @@ import { UPDATE_GAME, UPDATE_PLAYER, GAME_STATE, SET_SELECTED_CARD, SET_CARDS,
SET_MP_DIMS, MOVE_PLAYER, SET_NEXT_ACTION, NEXT_UI_ACTION, SET_MP_DIMS, MOVE_PLAYER, SET_NEXT_ACTION, NEXT_UI_ACTION,
MARK_ACTION_CHANGE_HANDLED, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED, MARK_ACTION_CHANGE_HANDLED, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED,
AUTO_SKIP, MESSAGE, SET_HARVEST_TABLE, SET_CARD_ERROR, AUTO_SKIP, MESSAGE, SET_HARVEST_TABLE, SET_CARD_ERROR,
SET_MOVING_SKIP } from './actionTypes.js' SET_MOVING_SKIP, SERVER_ERROR } from './actionTypes.js'
import { GAME_STATES } from '../../constants.js' import { GAME_STATES } from '../../constants.js'
import { spaceContent, corners } from 'game.js' import { spaceContent, corners } from 'game.js'
@ -124,7 +124,8 @@ const initialState = {
autoSkip: false, autoSkip: false,
playerSpaces: {}, playerSpaces: {},
movingSkip: false, movingSkip: false,
harvestTable: false }, harvestTable: false,
exn: false },
spaces: spaces, spaces: spaces,
space: null, space: null,
// message panel dimenions // message panel dimenions
@ -228,6 +229,8 @@ export default function(state = initialState, action) {
return { ...state, ui: { ...state.ui, harvestTable: action.table }}; return { ...state, ui: { ...state.ui, harvestTable: action.table }};
case SET_MOVING_SKIP: case SET_MOVING_SKIP:
return { ...state, ui: { ...state.ui, movingSkip: action.skip }}; return { ...state, ui: { ...state.ui, movingSkip: action.skip }};
case SERVER_ERROR:
return { ...state, ui: { ...state.ui, exn: action.exn }};
default: default:
return state; return state;
} }

@ -25,6 +25,8 @@ import LoginOrCreateAccount from '../login-or-create-account/LoginOrCreateAccoun
import { startOrJoinGame } from '../start/actions.js' import { startOrJoinGame } from '../start/actions.js'
import { start } from '../app/actions.js' import { start } from '../app/actions.js'
import { itemCardShort } from 'game.js'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faArrowCircleLeft, faCog } from '@fortawesome/free-solid-svg-icons' import { faArrowCircleLeft, faCog } from '@fortawesome/free-solid-svg-icons'
@ -63,6 +65,7 @@ class NewGame extends React.Component {
auditThreshold: 250000, auditThreshold: 250000,
startingCash: 5000, startingCash: 5000,
startingDebt: 5000, startingDebt: 5000,
startingOtbs: 2,
trade: true, trade: true,
showLogin: false showLogin: false
}; };
@ -71,10 +74,10 @@ class NewGame extends React.Component {
handleInputChange = e => { handleInputChange = e => {
const target = e.target, const target = e.target,
value = target.type === 'checkbox' && target.name !== 'trade' value = target.type === 'checkbox' && target.name !== 'trade'
? target.name : target.value, ? target.name : target.value,
name = target.type === 'checkbox' && target.name !== 'trade' name = target.type === 'checkbox' && target.name !== 'trade'
? 'checkedColor' : target.name; ? 'checkedColor' : target.name;
this.setState({ this.setState({
[name]: value [name]: value
@ -98,122 +101,129 @@ class NewGame extends React.Component {
render() { render() {
let titleBar = !this.props.hideBack ? ( let titleBar = !this.props.hideBack ? (
<Fragment> <Fragment>
<a onClick={this.handleBack}> <a onClick={this.handleBack}>
<FontAwesomeIcon icon={faArrowCircleLeft} /> <FontAwesomeIcon icon={faArrowCircleLeft} />
</a> </a>
{this.props.title} {this.props.title}
</Fragment> </Fragment>
) : this.props.title, ) : this.props.title,
colors = this.props.colors.map(c => ( colors = this.props.colors.map(c => (
<label key={c} className={'player player-selectable player-' + c + (this.state.checkedColor === c ? ' player-selected' : '')}> <label key={c} className={'player player-selectable player-' + c + (this.state.checkedColor === c ? ' player-selected' : '')}>
<input type='checkbox' <input type='checkbox'
checked={this.state.checkedColor === c} checked={this.state.checkedColor === c}
onChange={this.handleInputChange} onChange={this.handleInputChange}
name={c} /> name={c} />
</label> </label>
) )
), ),
gameName = this.props.showGameName && ( gameName = this.props.showGameName && (
<Row> <Row>
<Col width='12'> <Col width='12'>
<label>Game Name <label>Game Name
<input type='text' name='gameName' value={this.state.gameName} <input type='text' name='gameName' value={this.state.gameName}
required required
onChange={this.handleInputChange} /> onChange={this.handleInputChange} />
</label> </label>
</Col> </Col>
</Row> </Row>
), ),
settingsClass = this.state.showSettings ? '' : 'hidden', settingsClass = this.state.showSettings ? '' : 'hidden',
mainScreenClass = !this.state.showSettings ? '' : 'hidden'; mainScreenClass = !this.state.showSettings ? '' : 'hidden';
return ( return (
<GroupBox title={titleBar}> <GroupBox title={titleBar}>
{this.props.user ? ( {this.props.user ? (
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<div className={mainScreenClass}> <div className={mainScreenClass}>
<Row> <Row>
<Col width='12'> <Col width='12'>
<label>Your Color</label> <label>Your Color</label>
{colors} {colors}
<br /><br /> <br /><br />
</Col> </Col>
</Row> </Row>
{gameName} {gameName}
</div> </div>
<div className={settingsClass}> <div className={settingsClass}>
<InputRow label='Down Payment' <InputRow label='Down Payment'
name='downPayment' name='downPayment'
min={0} min={0}
max={1} max={1}
step={0.1} step={0.1}
value={this.state.downPayment} value={this.state.downPayment}
onChange={this.handleInputChange} /> onChange={this.handleInputChange} />
<InputRow label='Loan Interest' <InputRow label='Loan Interest'
name='loanInterest' name='loanInterest'
min={0} min={0}
max={1} max={1}
step={0.1} step={0.1}
value={this.state.loanInterest} value={this.state.loanInterest}
onChange={this.handleInputChange} /> onChange={this.handleInputChange} />
<InputRow label='Maximum Debt' <InputRow label='Maximum Debt'
name='maxDebt' name='maxDebt'
min={0} min={0}
step={5000} step={5000}
value={this.state.maxDebt} value={this.state.maxDebt}
onChange={this.handleInputChange} /> onChange={this.handleInputChange} />
<InputRow label='Audit Threshold' <InputRow label='Audit Threshold'
name='auditThreshold' name='auditThreshold'
min={0} min={0}
step={25000} step={25000}
value={this.state.auditThreshold} value={this.state.auditThreshold}
onChange={this.handleInputChange} /> onChange={this.handleInputChange} />
<InputRow label='Starting Cash' <InputRow label='Starting Cash'
name='startingCash' name='startingCash'
min={0} min={0}
step={1000} step={1000}
value={this.state.startingCash} value={this.state.startingCash}
onChange={this.handleInputChange} /> onChange={this.handleInputChange} />
<InputRow label='Starting Debt' <InputRow label='Starting Debt'
name='startingDebt' name='startingDebt'
min={0} min={0}
step={1000} step={1000}
value={this.state.startingDebt} value={this.state.startingDebt}
onChange={this.handleInputChange} /> onChange={this.handleInputChange} />
<Row> <InputRow label={'Number of Starting ' + itemCardShort}
<Col width='12'> name='startingOtbs'
<label> min={0}
<input type='checkbox' max={8}
checked={this.state.trade} step={1}
onChange={this.handleInputChange} value={this.state.startingOtbs}
name='trade' /> onChange={this.handleInputChange} />
Enable Trading <Row>
</label> <Col width='12'>
</Col> <label>
</Row> <input type='checkbox'
</div> checked={this.state.trade}
<Row> onChange={this.handleInputChange}
<Col width='12'> name='trade' />
<div className='new-game-submit-container'> Enable Trading
<Button type='submit'>{this.props.button} Game</Button> </label>
{this.props.showGameName ? ( </Col>
<a onClick={this.toggleSettings}> </Row>
<FontAwesomeIcon icon={faCog} size='lg' /> </div>
</a> <Row>
) : (<Fragment />)} <Col width='12'>
</div> <div className='new-game-submit-container'>
</Col> <Button type='submit'>{this.props.button} Game</Button>
</Row> {this.props.showGameName ? (
</form> <a onClick={this.toggleSettings}>
) : ( <FontAwesomeIcon icon={faCog} size='lg' />
<> </a>
<span>Sign in or create account to continue</span> ) : (<Fragment />)}
<LoginOrCreateAccount login={this.props.login} </div>
createAccount={this.props.createAccount} </Col>
errors={this.props.errors} </Row>
/> </form>
</> ) : (
)} <>
<span>Sign in or create account to continue</span>
<LoginOrCreateAccount login={this.props.login}
createAccount={this.props.createAccount}
errors={this.props.errors}
/>
</>
)}
</GroupBox> </GroupBox>
); );
} }

@ -78,6 +78,7 @@ const unsubscribeNewOrJoinGame = store.subscribe(() => {
}); });
let autostart = new URL(window.location.href).searchParams.get('autostart'); let autostart = new URL(window.location.href).searchParams.get('autostart');
let rejoin = new URL(window.location.href).searchParams.get('reload-game');
function handleMessage(evt) { function handleMessage(evt) {
const data = JSON.parse(evt.data), const data = JSON.parse(evt.data),
@ -86,6 +87,8 @@ function handleMessage(evt) {
if (data.event === 'error') { if (data.event === 'error') {
console.log('error:' + data.exn); console.log('error:' + data.exn);
} else if (data.event === 'new-game-started') { } else if (data.event === 'new-game-started') {
history.replaceState({}, document.title, '/');
console.log('clearing auto-reload');
initialize(store, Ws.sendCommand); initialize(store, Ws.sendCommand);
Ws.setMainOnMessage(handleMessageFarm); Ws.setMainOnMessage(handleMessageFarm);
Ws.openSecondary('push-web-socket'); Ws.openSecondary('push-web-socket');
@ -111,6 +114,10 @@ function handleMessage(evt) {
gameName: data.games.games[0].name })); gameName: data.games.games[0].name }));
} }
} }
if (rejoin) {
store.dispatch(startOrJoinGame({ type: 'join-as-existing',
gameId: false }));
}
} }
} }

@ -18,7 +18,7 @@
;;; <https://www.gnu.org/licenses/>. ;;; <https://www.gnu.org/licenses/>.
(import chicken scheme srfi-1 data-structures) (import chicken scheme srfi-1 data-structures)
(use http-session srfi-69 coops uri-common (use http-session srfi-69 coops coops-utils uri-common
srfi-18 medea numbers spiffy spiffy-cookies srfi-18 medea numbers spiffy spiffy-cookies
intarweb pll sxml-transforms websockets miscmacros intarweb pll sxml-transforms websockets miscmacros
mailbox) mailbox)
@ -156,7 +156,8 @@
(audit-threshold . 250000) (audit-threshold . 250000)
(starting-cash . 5000) (starting-cash . 5000)
(starting-debt . 5000) (starting-debt . 5000)
(trade . #t)) (trade . #t)
(starting-otbs . 2))
accessor: game-settings) accessor: game-settings)
(mutex initform: (make-mutex 'game) accessor: game-mutex))) (mutex initform: (make-mutex 'game) accessor: game-mutex)))
@ -169,8 +170,8 @@
(mutex initform: (make-mutex 'app) accessor: app-mutex))) (mutex initform: (make-mutex 'app) accessor: app-mutex)))
(define (player->sexp player) (define (player->sexp player)
`((cash . ,(player-cash player)) `((cash . ,(inexact->exact (round (player-cash player))))
(debt . ,(player-debt player)) (debt . ,(inexact->exact (round (player-debt player))))
(space . ,(player-space player)) (space . ,(player-space player))
(previous-space . ,(player-previous-space player)) (previous-space . ,(player-previous-space player))
(state . ,(player-state player)) (state . ,(player-state player))
@ -247,12 +248,54 @@
'games (map sexp->game (alist-ref 'games x)) 'games (map sexp->game (alist-ref 'games x))
'last-game-id (alist-ref 'last-game-id x))) 'last-game-id (alist-ref 'last-game-id x)))
(define (save-app) (define (validate-game g)
(with-output-to-file "/home/tjhintz/app.scm" (assert (instance-of? g <game>))
(lambda () (assert (number? (game-id g)))
(write (app->sexp *app*))))) (assert (list? (game-players g)))
(for-each (lambda (p)
(assert (instance-of? p <player>))
(assert (number? (player-cash p)))
(assert (number? (player-display-cash p)))
(assert (= (player-cash p) (player-display-cash p)))
(assert (number? (player-debt p)))
(assert (number? (player-space p)))
(assert (number? (player-previous-space p)))
(assert (symbol? (player-state p)))
(assert (member (player-state p) '(turn-ended pre-turn mid-turn)))
(assert (boolean? (player-finished p)))
(assert (list? (player-assets p))) ;; TODO test assets
(assert (list? (player-ridges p)))
(assert (number? (player-harvest-mult p)))
(assert (list? (player-otbs p)))
(assert (list? (player-farmers-fates p)))
(assert (list? (player-year-rules p)))
(assert (list? (player-next-year-rules p)))
(assert (symbol? (player-color p)))
(assert (string? (player-name p)))
(assert (number? (player-user-id p)))
(assert (list? (player-trade p)))
(assert (number? (player-last-cash p)))
(assert (boolean? (player-harvesting p)))
(assert (boolean? (player-hay-doubled p)))
(assert (boolean? (player-corn-doubled p))))
(game-players g))
(assert (list? (game-otbs g)))
(assert (list? (game-used-otbs g)))
(assert (list? (game-farmers-fates g)))
(assert (list? (game-operating-expenses g)))
(assert (number? (game-operating-expense-index g)))
(assert (list? (game-colors g)))
(assert (or (instance-of? (game-called-audit g) <player>)
(boolean? (game-called-audit g))))
(assert (symbol? (game-state g))) ;; TODO test all symbols
(assert (string? (game-name g)))
(assert (number? (game-turn g)))
(assert (or (instance-of? (game-current-player g) <player>)
(boolean? (game-current-player g))))
(assert (list? (game-settings g))))
(define (save-game game) (define (save-game game)
(validate-game game)
(db-update-game (game-id game) (symbol->string (game-state game)) (db-update-game (game-id game) (symbol->string (game-state game))
(game->sexp game))) (game->sexp game)))
@ -293,7 +336,9 @@
(set-cookie! (session-cookie-name) sid)))) (set-cookie! (session-cookie-name) sid))))
(session-lifetime (* 60 60 24 7 4)) (session-lifetime (* 60 60 24 7 4))
(access-log (current-output-port)) ;; (access-log (current-output-port))
(access-log "access.log")
(error-log "error.log")
(handle-not-found (handle-not-found
(let ((old-handler (handle-not-found))) (let ((old-handler (handle-not-found)))
@ -577,14 +622,21 @@
(lambda (p1 p2) (lambda (p1 p2)
(> (player-net-worth p1) (> (player-net-worth p1)
(player-net-worth p2)))))) (player-net-worth p2))))))
(bonus (max (farming-round ;; (bonus (max (farming-round
(* (- (player-net-worth richest) ;; (inexact->exact
(player-net-worth player)) ;; (round
0.2)) ;; (* (- (player-net-worth richest)
2500))) ;; (+ (player-net-worth player)
;; ;; don't give a bonus for emergency debt
;; (max 0 (- (player-debt player) (game-setting 'max-debt game)))))
;; 0.2))))
;; 2500))
(bonus 5000)
)
(safe-set! (player-cash player) (safe-set! (player-cash player)
;; (+ (player-cash player) 5000) (+ (player-cash player) 5000)
(+ (player-cash player) bonus)) ;; (+ (player-cash player) bonus)
)
(safe-set! (player-display-cash player) (player-cash player)) (safe-set! (player-display-cash player) (player-cash player))
(safe-set! (game-actions game) (safe-set! (game-actions game)
(cons `((?action . info) (cons `((?action . info)
@ -827,8 +879,8 @@
#f))) #f)))
(define (call-audit game player) (define (call-audit game player)
(if (game-called-audit game) (if (not (game-called-audit game))
(begin (safe-set! (game-called-audit game) player)))) (safe-set! (game-called-audit game) player)))
(define (player-net-worth player) (define (player-net-worth player)
(+ (* (+ (player-asset 'hay player) (player-asset 'grain player)) 2000) (+ (* (+ (player-asset 'hay player) (player-asset 'grain player)) 2000)
@ -942,6 +994,17 @@
rolls)) rolls))
(_make-rolls n 1 (list (next-roll -1)))) (_make-rolls n 1 (list (next-roll -1))))
(define (log-error exn)
(with-output-to-file (error-log)
(lambda ()
(print-call-chain)
(print exn)
(print-error-message exn))
append:))
(define (log-msg msg)
(log-to (error-log) "~A" msg))
(define (process-message player game type msg) (define (process-message player game type msg)
(when player (when player
(safe-set! (player-last-cash player) (player-cash player))) (safe-set! (player-last-cash player) (player-cash player)))
@ -1253,10 +1316,8 @@
(begin (advance-turn game player) (begin (advance-turn game player)
(handle-exceptions (handle-exceptions
exn exn
(begin (print-call-chain) (begin (log-error exn)
(print exn) (log-msg "error saving app"))
(print-error-message exn)
(print "error saving app"))
(save-game game)) (save-game game))
(if (eq? (game-state game) 'finished) (if (eq? (game-state game) 'finished)
(do-end-of-game game) (do-end-of-game game)
@ -1286,6 +1347,10 @@
0)) 0))
(starting-debt . ,(->i (alist-ref 'startingDebt msg) (starting-debt . ,(->i (alist-ref 'startingDebt msg)
0)) 0))
(starting-otbs . ,(min (max (->number (alist-ref 'startingOtbs msg)
2)
0)
8))
(trade . ,(or (alist-ref 'trade msg) #t))))) (trade . ,(or (alist-ref 'trade msg) #t)))))
(player (add-player-to-game game (player (add-player-to-game game
color color
@ -1301,7 +1366,7 @@
(session-set! (sid) 'game-id (game-id game))) (session-set! (sid) 'game-id (game-id game)))
(*game* game) (*game* game)
(*player* player) (*player* player)
(set-startup-otbs game player 2) (set-startup-otbs game player (alist-ref 'starting-otbs (game-settings game)))
;; (set-startup-otbs game ai-player 2) ;; (set-startup-otbs game ai-player 2)
;; (thread-start! (make-ai-push-receiver game ai-player)) ;; (thread-start! (make-ai-push-receiver game ai-player))
(create-start-response "new-game-started"))) (create-start-response "new-game-started")))
@ -1323,11 +1388,12 @@
(db-add-user-game (alist-ref 'id user) (game-id game)) (db-add-user-game (alist-ref 'id user) (game-id game))
(*game* game) (*game* game)
(*player* player) (*player* player)
(set-startup-otbs game player 2) (set-startup-otbs game player (alist-ref 'starting-otbs (game-settings game)))
(message-players! game player '() type: "update") (message-players! game player '() type: "update")
(create-start-response "new-game-started"))) (create-start-response "new-game-started")))
((string=? type "join-as-existing") ((string=? type "join-as-existing")
(let* ((id (alist-ref 'gameId msg)) (let* ((id (or (alist-ref 'gameId msg)
(session-ref (sid) 'game-id)))
(user-id (session-ref (sid) 'user-id)) (user-id (session-ref (sid) 'user-id))
(game (find-game id)) (game (find-game id))
(player (find (lambda (p) (equal? (player-user-id p) user-id)) (player (find (lambda (p) (equal? (player-user-id p) user-id))
@ -1440,19 +1506,19 @@
exn exn
(send-message (send-message
(json->string (json->string
`((exn . ,(with-output-to-string `((exn . ,(begin (log-error exn)
(lambda () (conc "Server error: " (with-output-to-string
(print-call-chain) (lambda ()
(print-error-message exn)))) (print-error-message exn))))))
(event . "error")))) (event . "error"))))
(send-message (send-message
(json->string (json->string
(handle-exceptions (handle-exceptions
exn exn
`((exn . ,(with-output-to-string `((exn . ,(begin (log-error exn)
(lambda () (conc "Server error: " (with-output-to-string
(print-call-chain) (lambda ()
(print-error-message exn)))) (print-error-message exn))))))
(event . "error")) (event . "error"))
(session-game) (session-game)
(let* ((game (*game*)) (let* ((game (*game*))
@ -1492,18 +1558,18 @@
exn exn
(send-message (send-message
(json->string (json->string
`((exn . ,(with-output-to-string `((exn . ,(begin (log-error exn)
(lambda () (conc "Server error: " (with-output-to-string
(print-call-chain) (lambda ()
(print-error-message exn))))))) (print-error-message exn)))))))))
(send-message (send-message
(json->string (json->string
(handle-exceptions (handle-exceptions
exn exn
`((exn . ,(with-output-to-string `((exn . ,(begin (log-error exn)
(lambda () (conc "Server error: " (with-output-to-string
(print-call-chain) (lambda ()
(print-error-message exn)))) (print-error-message exn))))))
(event . "error")) (event . "error"))
(create-ws-response (*player*) (create-ws-response (*player*)
(alist-ref 'type msg) (alist-ref 'type msg)
@ -2143,7 +2209,10 @@
(let ((value (alist-ref '?value action))) (let ((value (alist-ref '?value action)))
(if (procedure? value) (if (procedure? value)
(value player) (value player)
(apply (alist-ref (car value) *action-map*) player (cdr value))))) (let ((action-proc (alist-ref (car value) *action-map*)))
(if (procedure? action-proc)
(apply action-proc player (cdr value))
(print (conc "unknown action value: " value)))))))
((eq? a 'harvest-mult) ((eq? a 'harvest-mult)
(safe-set! (player-harvest-mult player) (safe-set! (player-harvest-mult player)
(* (player-harvest-mult player) (alist-ref '?value action)))) (* (player-harvest-mult player) (alist-ref '?value action))))
@ -2314,3 +2383,207 @@
;; Error: (assv) bad argument type: #<coops instance of `<game>'> ;; Error: (assv) bad argument type: #<coops instance of `<game>'>
;; when getting trade the name is wrong ;; when getting trade the name is wrong
;; error:
;; Call history:
;; farm.scm:482: player-last-cash
;; farm.scm:99: coops#slot-value
;; farm.scm:483: player-hay-doubled
;; farm.scm:99: coops#slot-value
;; farm.scm:484: player-corn-doubled
;; farm.scm:99: coops#slot-value
;; farm.scm:513: list->vector
;; farm.scm:517: game-called-audit
;; farm.scm:135: coops#slot-value
;; farm.scm:518: game-called-audit
;; farm.scm:135: coops#slot-value
;; farm.scm:518: player-name
;; farm.scm:1450: k8469
;; farm.scm:1450: g8473
;; farm.scm:1452: with-output-to-string
;; farm.scm:1454: print-call-chain <--
;; Error: (player-name) no method defined for given argument classes: (#t)
;; clicking roll button
;; start game with more otbs
;; Error: (assv) bad argument type: #f
;; Call history:
;; numbers.scm:1672: scan-real
;; numbers.scm:1671: scan-ureal
;; numbers.scm:1603: scan-digits+hashes
;; numbers.scm:1549: scan-digits
;; numbers.scm:1531: lp
;; numbers.scm:1531: lp
;; numbers.scm:1548: g1937
;; numbers.scm:1720: %string->compnum
;; numbers.scm:1672: scan-real
;; numbers.scm:1671: scan-ureal
;; numbers.scm:1603: scan-digits+hashes
;; numbers.scm:1549: scan-digits
;; numbers.scm:1531: lp
;; numbers.scm:1548: g1937
;; farm.scm:50: expiration
;; farm.scm:43: current-milliseconds
;; farm.scm:44: http-session#session-lifetime
;; farm.scm:44: numbers#*
;; numbers.scm:382: %*
;; farm.scm:44: numbers#floor
;; farm.scm:44: numbers#inexact->exact
;; numbers.scm:867: exact?
;; farm.scm:43: numbers#+
;; numbers.scm:295: %+
;; farm.scm:50: spiffy#remote-address
;; farm.scm:50: http-session#make-session-item
;; farm.scm:1441: *game*
;; farm.scm:1449: *game*
;; farm.scm:1476: *game*
;; farm.scm:1477: *player*
;; farm.scm:1479: alist-ref
;; farm.scm:1468: k8474
;; farm.scm:1468: g8478
;; farm.scm:1470: log-error
;; farm.scm:954: spiffy#error-log
;; farm.scm:954: with-output-to-file
;; farm.scm:956: print-call-chain
;; farm.scm:957: print
;; farm.scm:958: print-error-message
;; farm.scm:1471: with-output-to-string
;; farm.scm:1473: print-error-message
;; farm.scm:1471: conc
;; farm.scm:1467: medea#json->string
;; farm.scm:1466: websockets#send-message
;; farm.scm:1455: k8462
;; farm.scm:1455: g8466
;; farm.scm:1461: log-error
;; farm.scm:954: spiffy#error-log
;; farm.scm:954: with-output-to-file
;; farm.scm:956: print-call-chain <--
;; #<condition: (exn i/o net)>
;; Error: cannot write to socket - Broken pipe: 7
;; [Sat Apr 11 23:04:48 2020] "GET http://localhost:8080/websocket/web-socket HTTP/1.1" Uncaught exception:
;; #<condition: (uncaught-exception)>
;; Call history:
;; farm.scm:164: game-settings
;; farm.scm:135: coops#slot-value
;; farm.scm:164: alist-ref
;; farm.scm:164: game-settings
;; farm.scm:135: coops#slot-value
;; farm.scm:164: alist-ref
;; farm.scm:164: game-settings
;; farm.scm:135: coops#slot-value
;; farm.scm:164: alist-ref
;; farm.scm:164: game-settings
;; farm.scm:135: coops#slot-value
;; farm.scm:164: alist-ref
;; farm.scm:164: game-settings
;; farm.scm:135: coops#slot-value
;; farm.scm:164: alist-ref
;; farm.scm:866: append
;; farm.scm:1483: game-last-updated
;; farm.scm:135: coops#slot-value
;; farm.scm:1483: numbers#+
;; numbers.scm:295: %+
;; farm.scm:1483: game-mutex
;; farm.scm:135: coops#slot-value
;; farm.scm:1483: dynamic-wind
;; farm.scm:1483: mutex-lock!
;; farm.scm:1483: ##sys#setter
;; farm.scm:1483: g8506
;; farm.scm:135: coops#set-slot-value!
;; farm.scm:1483: mutex-unlock!
;; farm.scm:1484: *player*
;; farm.scm:1485: *player*
;; farm.scm:1485: game-last-updated
;; farm.scm:135: coops#slot-value
;; farm.scm:1485: player-mutex
;; farm.scm:99: coops#slot-value
;; farm.scm:1485: dynamic-wind
;; farm.scm:1485: mutex-lock!
;; farm.scm:1485: ##sys#setter
;; farm.scm:1485: g8526
;; farm.scm:99: coops#set-slot-value!
;; farm.scm:1485: mutex-unlock!
;; tmp213217
;; farm.scm:1469: k8475
;; farm.scm:1469: g8479
;; farm.scm:1468: medea#json->string
;; farm.scm:1456: k8463
;; farm.scm:1456: g8467
;; farm.scm:1462: log-error
;; farm.scm:955: spiffy#error-log
;; farm.scm:955: with-output-to-file
;; farm.scm:957: print-call-chain <--
;; #<condition: (exn type)>
;; Error: (symbol->string) bad argument type - not a symbol: (contents . "\n<p>LEASE Marineris Ridge</p>\n<p>for 30 years at $20,000</p>\n<p>...
;; rror: (assv) bad argument type: pre-turn
;; Call history:
;; sql-de-lite.scm:600: statement-ptr
;; sql-de-lite.scm:609: statement-ptr
;; sql-de-lite.scm:222: ##sys#block-set!
;; sql-de-lite.scm:612: remove-active-statement!
;; sql-de-lite.scm:202: hash-table-delete!
;; sql-de-lite.scm:893: for-each-active-statement
;; sql-de-lite.scm:204: hash-table-walk
;; sql-de-lite.scm:172: ##sys#block-set!
;; sql-de-lite.scm:897: object-release
;; sql-de-lite.scm:172: ##sys#block-set!
;; db.scm:28: alist-ref
;; db.scm:27: with-input-from-string
;; numbers.scm:1720: %string->compnum
;; numbers.scm:1672: scan-real
;; numbers.scm:1671: scan-ureal
;; numbers.scm:1603: scan-digits+hashes
;; numbers.scm:1549: scan-digits
;; numbers.scm:1531: lp
;; numbers.scm:1531: lp
;; numbers.scm:1548: g1937
;; numbers.scm:1720: %string->compnum
;; numbers.scm:1672: scan-real
;; numbers.scm:1671: scan-ureal
;; numbers.scm:1603: scan-digits+hashes
;; numbers.scm:1549: scan-digits
;; numbers.scm:1531: lp
;; numbers.scm:1548: g1937
;; farm.scm:50: expiration
;; farm.scm:43: current-milliseconds
;; farm.scm:44: http-session#session-lifetime
;; farm.scm:44: numbers#*
;; numbers.scm:382: %*
;; farm.scm:44: numbers#floor
;; farm.scm:44: numbers#inexact->exact
;; numbers.scm:867: exact?
;; farm.scm:43: numbers#+
;; numbers.scm:295: %+
;; farm.scm:50: spiffy#remote-address
;; farm.scm:50: http-session#make-session-item
;; farm.scm:1484: *game*
;; farm.scm:1492: *game*
;; farm.scm:1519: *game*
;; farm.scm:1520: *player*
;; farm.scm:1522: alist-ref
;; farm.scm:1511: k8652
;; farm.scm:1511: g8656
;; farm.scm:1513: log-error
;; farm.scm:997: spiffy#error-log
;; farm.scm:997: with-output-to-file
;; farm.scm:999: print-call-chain <--
;; #<condition: (exn type)>

@ -24,15 +24,28 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyPlugin = require('copy-webpack-plugin'); const CopyPlugin = require('copy-webpack-plugin');
const webpack = require("webpack"); const webpack = require("webpack");
const autoprefixer = require("autoprefixer"); const autoprefixer = require("autoprefixer");
const CssUrlRelativePlugin = require('css-url-relative-plugin')
module.exports = { module.exports = {
entry: { entry: {
app: './src/main.jsx', app: './src/main.jsx',
}, },
output: { output: {
filename: '[name].bundle.js', filename: './assets/[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, 'dist'),
}, },
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
plugins: [ plugins: [
new CleanWebpackPlugin(), new CleanWebpackPlugin(),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
@ -42,8 +55,8 @@ module.exports = {
}), }),
new FaviconsWebpackPlugin('./assets/img/tractor.svg'), new FaviconsWebpackPlugin('./assets/img/tractor.svg'),
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
filename: '[name].css', filename: './assets/[name].[contenthash].css',
chunkFilename: '[id].css', chunkFilename: './assets/[id].[contenthash].css',
}), }),
new CopyPlugin([ new CopyPlugin([
{ from: './src/server/farm.scm', to: './[name].[ext]' }, { from: './src/server/farm.scm', to: './[name].[ext]' },
@ -57,6 +70,7 @@ module.exports = {
] ]
} }
}), }),
new CssUrlRelativePlugin()
], ],
module: { module: {
rules: [ rules: [
@ -71,11 +85,19 @@ module.exports = {
} }
} }
}, },
// {
// test: /mars-texture.png$/,
// loader: 'file-loader',
// options: {
// name: './assets/img/[name].[contenthash].[ext]',
// },
// },
{ {
test: /\.(woff|woff2|eot|ttf|otf|svg|png|gif)$/, test: /\.(woff|woff2|eot|ttf|otf|svg|png|gif)$/,
use: [ loader: 'file-loader',
'file-loader', options: {
], name: './assets/img/[name].[contenthash].[ext]',
},
}, },
{ {
test: /\.s[ac]ss$/i, test: /\.s[ac]ss$/i,

Loading…
Cancel
Save