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
runprod:
cd dist/ && chmod +x farm && ./farm
cd dist/ && chmod +x farm && ./farm -:a50
upload:
rsync -rtvz dist/ $(SERVER):~/farm

@ -47,7 +47,7 @@
"20 Cows on Peridier Ridge"))))
(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 "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.")))

53
package-lock.json generated

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

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

@ -348,6 +348,7 @@ class PlayerSummary extends React.Component {
<PlayerTurnContainer player={player}
game={this.props.game}
ui={this.props.ui}
hideForLarge={this.props.hideForLarge}
screen={this.props.screen}
showScreen={this.props.showScreen}/>
</GroupBox>
@ -426,7 +427,9 @@ class PlayerTurnContainer extends React.Component {
<br />
</>
) : (<></>)}
{view}
<div className={this.props.hideForLarge ? 'hide-for-large' : ''}>
{view}
</div>
</div>
</Col>
</Row>
@ -1885,7 +1888,7 @@ class Info extends React.Component {
const SCREENS = { summary: 'summary', misc: 'misc', farms: 'farms',
cards: 'cards', trade: 'trade', loans: 'loans',
action: 'action', info: 'info' };
action: 'action', info: 'info', error: 'error' };
class BoardApp extends React.Component {
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;
this.setState({ currentPlayer });
}
if (!prevProps.ui.exn && this.props.ui.exn) {
this.setState({ screen: SCREENS.error });
}
}
componentDidMount() {
@ -2170,6 +2177,7 @@ class BoardApp extends React.Component {
<PlayerSummary player={this.props.player}
ui={this.props.ui}
screen={this.state.screen}
hideForLarge={true}
game={this.props.game} showScreen={this.showScreen} />
</div>
<div className={this.tabClass(SCREENS.action)}>
@ -2212,6 +2220,11 @@ class BoardApp extends React.Component {
<div className={this.tabClass(SCREENS.info)}>
<Info harvestTable={this.props.ui.harvestTable} game={this.props.game} />
</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 className='static-tab-container show-for-large'>
<div className='tab show'>

@ -39,3 +39,4 @@ export const AUTO_SKIP = 'auto-skip'
export const MESSAGE = 'message'
export const SET_HARVEST_TABLE = 'set-harvest-table'
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,
MOVE_PLAYER, NEXT_UI_ACTION, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED,
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,
spacePushPlayer, spaceClearPlayers, setOldMessages, setMessagePanelSpace,
mpMouse, setMPDims, movePlayer, setNextAction, nextUIAction,
markActionChangeHandled, nextUIActionSilent, alert, alertHandled,
autoSkip, message, setHarvestTable, setCardError, setMovingSkip }
autoSkip, message, setHarvestTable, setCardError, setMovingSkip,
serverError }
function updateGame(update) {
return { type: UPDATE_GAME,
@ -135,3 +136,7 @@ function setHarvestTable(table) {
function setMovingSkip(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,
mpMouse, rolled, setNextAction, nextUIAction, nextUIActionSilent, alert,
autoSkip, message, alertHandled, setHarvestTable,
setCardError, setMovingSkip } from './actions.js'
setCardError, setMovingSkip, serverError } from './actions.js'
import { itemCard, fateCard } from 'game.js'
export { initialize, buy, roll, endTurn, loan, trade, submitTradeAccept,
@ -42,6 +42,7 @@ function handleMessage(evt) {
if (data.event === 'error') {
console.log('error:' + data.exn);
store.dispatch(serverError(data.exn));
return;
}
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,
MARK_ACTION_CHANGE_HANDLED, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED,
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 { spaceContent, corners } from 'game.js'
@ -124,7 +124,8 @@ const initialState = {
autoSkip: false,
playerSpaces: {},
movingSkip: false,
harvestTable: false },
harvestTable: false,
exn: false },
spaces: spaces,
space: null,
// message panel dimenions
@ -228,6 +229,8 @@ export default function(state = initialState, action) {
return { ...state, ui: { ...state.ui, harvestTable: action.table }};
case SET_MOVING_SKIP:
return { ...state, ui: { ...state.ui, movingSkip: action.skip }};
case SERVER_ERROR:
return { ...state, ui: { ...state.ui, exn: action.exn }};
default:
return state;
}

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

@ -78,6 +78,7 @@ const unsubscribeNewOrJoinGame = store.subscribe(() => {
});
let autostart = new URL(window.location.href).searchParams.get('autostart');
let rejoin = new URL(window.location.href).searchParams.get('reload-game');
function handleMessage(evt) {
const data = JSON.parse(evt.data),
@ -86,6 +87,8 @@ function handleMessage(evt) {
if (data.event === 'error') {
console.log('error:' + data.exn);
} else if (data.event === 'new-game-started') {
history.replaceState({}, document.title, '/');
console.log('clearing auto-reload');
initialize(store, Ws.sendCommand);
Ws.setMainOnMessage(handleMessageFarm);
Ws.openSecondary('push-web-socket');
@ -111,6 +114,10 @@ function handleMessage(evt) {
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/>.
(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
intarweb pll sxml-transforms websockets miscmacros
mailbox)
@ -156,7 +156,8 @@
(audit-threshold . 250000)
(starting-cash . 5000)
(starting-debt . 5000)
(trade . #t))
(trade . #t)
(starting-otbs . 2))
accessor: game-settings)
(mutex initform: (make-mutex 'game) accessor: game-mutex)))
@ -169,8 +170,8 @@
(mutex initform: (make-mutex 'app) accessor: app-mutex)))
(define (player->sexp player)
`((cash . ,(player-cash player))
(debt . ,(player-debt player))
`((cash . ,(inexact->exact (round (player-cash player))))
(debt . ,(inexact->exact (round (player-debt player))))
(space . ,(player-space player))
(previous-space . ,(player-previous-space player))
(state . ,(player-state player))
@ -247,12 +248,54 @@
'games (map sexp->game (alist-ref 'games x))
'last-game-id (alist-ref 'last-game-id x)))
(define (save-app)
(with-output-to-file "/home/tjhintz/app.scm"
(lambda ()
(write (app->sexp *app*)))))
(define (validate-game g)
(assert (instance-of? g <game>))
(assert (number? (game-id g)))
(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)
(validate-game game)
(db-update-game (game-id game) (symbol->string (game-state game))
(game->sexp game)))
@ -293,7 +336,9 @@
(set-cookie! (session-cookie-name) sid))))
(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
(let ((old-handler (handle-not-found)))
@ -577,14 +622,21 @@
(lambda (p1 p2)
(> (player-net-worth p1)
(player-net-worth p2))))))
(bonus (max (farming-round
(* (- (player-net-worth richest)
(player-net-worth player))
0.2))
2500)))
;; (bonus (max (farming-round
;; (inexact->exact
;; (round
;; (* (- (player-net-worth richest)
;; (+ (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)
;; (+ (player-cash player) 5000)
(+ (player-cash player) bonus))
(+ (player-cash player) 5000)
;; (+ (player-cash player) bonus)
)
(safe-set! (player-display-cash player) (player-cash player))
(safe-set! (game-actions game)
(cons `((?action . info)
@ -827,8 +879,8 @@
#f)))
(define (call-audit game player)
(if (game-called-audit game)
(begin (safe-set! (game-called-audit game) player))))
(if (not (game-called-audit game))
(safe-set! (game-called-audit game) player)))
(define (player-net-worth player)
(+ (* (+ (player-asset 'hay player) (player-asset 'grain player)) 2000)
@ -942,6 +994,17 @@
rolls))
(_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)
(when player
(safe-set! (player-last-cash player) (player-cash player)))
@ -1253,10 +1316,8 @@
(begin (advance-turn game player)
(handle-exceptions
exn
(begin (print-call-chain)
(print exn)
(print-error-message exn)
(print "error saving app"))
(begin (log-error exn)
(log-msg "error saving app"))
(save-game game))
(if (eq? (game-state game) 'finished)
(do-end-of-game game)
@ -1286,6 +1347,10 @@
0))
(starting-debt . ,(->i (alist-ref 'startingDebt msg)
0))
(starting-otbs . ,(min (max (->number (alist-ref 'startingOtbs msg)
2)
0)
8))
(trade . ,(or (alist-ref 'trade msg) #t)))))
(player (add-player-to-game game
color
@ -1301,7 +1366,7 @@
(session-set! (sid) 'game-id (game-id game)))
(*game* game)
(*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)
;; (thread-start! (make-ai-push-receiver game ai-player))
(create-start-response "new-game-started")))
@ -1323,11 +1388,12 @@
(db-add-user-game (alist-ref 'id user) (game-id game))
(*game* game)
(*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")
(create-start-response "new-game-started")))
((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))
(game (find-game id))
(player (find (lambda (p) (equal? (player-user-id p) user-id))
@ -1440,19 +1506,19 @@
exn
(send-message
(json->string
`((exn . ,(with-output-to-string
(lambda ()
(print-call-chain)
(print-error-message exn))))
`((exn . ,(begin (log-error exn)
(conc "Server error: " (with-output-to-string
(lambda ()
(print-error-message exn))))))
(event . "error"))))
(send-message
(json->string
(handle-exceptions
exn
`((exn . ,(with-output-to-string
(lambda ()
(print-call-chain)
(print-error-message exn))))
`((exn . ,(begin (log-error exn)
(conc "Server error: " (with-output-to-string
(lambda ()
(print-error-message exn))))))
(event . "error"))
(session-game)
(let* ((game (*game*))
@ -1492,18 +1558,18 @@
exn
(send-message
(json->string
`((exn . ,(with-output-to-string
(lambda ()
(print-call-chain)
(print-error-message exn)))))))
`((exn . ,(begin (log-error exn)
(conc "Server error: " (with-output-to-string
(lambda ()
(print-error-message exn)))))))))
(send-message
(json->string
(handle-exceptions
exn
`((exn . ,(with-output-to-string
(lambda ()
(print-call-chain)
(print-error-message exn))))
`((exn . ,(begin (log-error exn)
(conc "Server error: " (with-output-to-string
(lambda ()
(print-error-message exn))))))
(event . "error"))
(create-ws-response (*player*)
(alist-ref 'type msg)
@ -2143,7 +2209,10 @@
(let ((value (alist-ref '?value action)))
(if (procedure? value)
(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)
(safe-set! (player-harvest-mult player)
(* (player-harvest-mult player) (alist-ref '?value action))))
@ -2314,3 +2383,207 @@
;; Error: (assv) bad argument type: #<coops instance of `<game>'>
;; 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 webpack = require("webpack");
const autoprefixer = require("autoprefixer");
const CssUrlRelativePlugin = require('css-url-relative-plugin')
module.exports = {
entry: {
app: './src/main.jsx',
},
output: {
filename: '[name].bundle.js',
filename: './assets/[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
@ -42,8 +55,8 @@ module.exports = {
}),
new FaviconsWebpackPlugin('./assets/img/tractor.svg'),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
filename: './assets/[name].[contenthash].css',
chunkFilename: './assets/[id].[contenthash].css',
}),
new CopyPlugin([
{ from: './src/server/farm.scm', to: './[name].[ext]' },
@ -57,6 +70,7 @@ module.exports = {
]
}
}),
new CssUrlRelativePlugin()
],
module: {
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)$/,
use: [
'file-loader',
],
loader: 'file-loader',
options: {
name: './assets/img/[name].[contenthash].[ext]',
},
},
{
test: /\.s[ac]ss$/i,

Loading…
Cancel
Save