Skip to content

Commit e369fc5

Browse files
committed
fix(jsbattle-webpage): fix fights against sandbox opponnents for registered players
1 parent a7f554f commit e369fc5

File tree

10 files changed

+150
-117
lines changed

10 files changed

+150
-117
lines changed

packages/jsbattle-server/app/services/UserStore.service.js

+2
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class UserStoreService extends Service {
8585
}
8686
const reservedNames = [
8787
'jsbattle',
88+
'sandbox',
89+
'user',
8890
'admin'
8991
];
9092
const isNameReserved = reservedNames.indexOf(username.toLowerCase()) != -1;

packages/jsbattle-webpage/src/actions/actionTypes.js

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export const AI_SCRIPT_FAILURE = 'AI_SCRIPT_FAILURE';
2929
export const AI_SCRIPT_SUCCESS = 'AI_SCRIPT_SUCCESS';
3030
export const AI_SCRIPT_CHANGED_REQUEST = 'AI_SCRIPT_CHANGED_REQUEST';
3131
export const AI_SCRIPT_CHANGED_SUCCESS = 'AI_SCRIPT_CHANGED_SUCCESS';
32+
export const SANDBOX_OPPONENT_LIST = 'SANDBOX_OPPONENT_LIST';
33+
export const SANDBOX_OPPONENT_LIST_FAILURE = 'SANDBOX_OPPONENT_LIST_FAILURE';
3234
export const SANDBOX_OPPONENT_CHANGE = 'SANDBOX_OPPONENT_CHANGE';
3335
export const SANDBOX_OPPONENT_TEAM_MODE = 'SANDBOX_OPPONENT_TEAM_MODE';
3436
export const SANDBOX_OPPONENT_DUEL_MODE = 'SANDBOX_OPPONENT_DUEL_MODE';

packages/jsbattle-webpage/src/actions/coreAction.js

-4
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,6 @@ export const getSettings = () => {
5757

5858
let settings = await profileService.getSettings();
5959

60-
let result = await fetch("tanks/index.json");
61-
let bundledTanks = await result.json();
62-
settings.bundledTanks = bundledTanks;
63-
6460
dispatch({type: SETTINGS_SUCCESS, payload: settings});
6561
};
6662
};

packages/jsbattle-webpage/src/actions/sandboxAction.js

+87-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import aiRepoService from "../services/aiRepoService.js";
22
import statsService from '../services/statsService.js';
33
import {
44
SANDBOX_OPPONENT_CHANGE,
5+
SANDBOX_OPPONENT_LIST,
6+
SANDBOX_OPPONENT_LIST_FAILURE,
57
SANDBOX_OPPONENT_TEAM_MODE,
68
SANDBOX_OPPONENT_DUEL_MODE,
79
SANDBOX_RNG_LOCK,
@@ -115,19 +117,96 @@ export const renameAiScript = (newName, id, useRemoteService) => {
115117
);
116118
};
117119

118-
export const setSandboxOpponent = (type, name) => {
120+
export const getSandboxOpponentList = (useRemoteService) => {
119121
return async (dispatch) => {
120-
let code = '';
121-
if(type == 'user') {
122-
code = await aiRepoService.getScript(name, 'scriptMap');
123-
code = code.code;
122+
let result;
123+
let userTankList = [];
124+
if(useRemoteService) {
125+
try {
126+
result = await fetch("/api/user/scripts", {});
127+
if(!result.ok) {
128+
throw new Error(`Error ${result.status}: ${result.statusText} (url: ${result.url})`);
129+
}
130+
userTankList = await result.json();
131+
userTankList = userTankList.map((script) => ({
132+
id: script.id,
133+
label: "sandbox/" + script.scriptName,
134+
source: 'remote_user'
135+
}));
136+
} catch(err) {
137+
console.log(err);
138+
return dispatch({
139+
type: SANDBOX_OPPONENT_LIST_FAILURE,
140+
payload: err
141+
});
142+
}
143+
} else {
144+
userTankList = await aiRepoService.getScriptNameList();
145+
userTankList = userTankList.map((script) => ({
146+
id: script.id,
147+
label: 'sandbox/' + script.scriptName,
148+
source: 'local_user'
149+
}));
150+
}
151+
152+
result = await fetch("tanks/index.json");
153+
let bundledTanks = await result.json();
154+
155+
bundledTanks = bundledTanks.map((name) => ({
156+
id: name,
157+
label: "jsbattle/" + name,
158+
source: "bundled"
159+
}));
160+
161+
dispatch({
162+
type: SANDBOX_OPPONENT_LIST,
163+
payload: Array.concat(bundledTanks, userTankList)
164+
});
165+
};
166+
};
167+
168+
export const setSandboxOpponent = (source, id) => {
169+
return async (dispatch) => {
170+
let scriptName;
171+
let scriptCode;
172+
let script;
173+
switch(source) {
174+
case 'local_user':
175+
script = await aiRepoService.getScript(id, 'scriptMap');
176+
scriptName = script.scriptName;
177+
scriptCode = script.code;
178+
break;
179+
case 'bundled':
180+
scriptName = id;
181+
scriptCode = '';
182+
break;
183+
case 'remote_user':
184+
try {
185+
script = await fetch("/api/user/scripts/" + id);
186+
if(!script.ok) {
187+
throw new Error(`Error ${script.status}: ${script.statusText} (url: ${script.url})`);
188+
}
189+
script = await script.json();
190+
scriptName = script.scriptName;
191+
scriptCode = script.code;
192+
} catch(err) {
193+
console.log(err);
194+
return dispatch({
195+
type: SANDBOX_OPPONENT_LIST_FAILURE,
196+
payload: err
197+
});
198+
}
199+
break;
200+
default:
201+
throw new Error(`Not supported source ${source}`);
124202
}
125203
dispatch({
126204
type: SANDBOX_OPPONENT_CHANGE,
127205
payload: {
128-
type,
129-
name,
130-
code
206+
id,
207+
source,
208+
name: scriptName,
209+
code: scriptCode
131210
}
132211
});
133212
};

packages/jsbattle-webpage/src/components/LiveCodeSandboxSettingsTab.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import Row from './Row.js';
66
class LiveCodeSandboxSettingsTab extends React.Component {
77

88
render() {
9-
let opponents = this.props.opponents.map((opponent) => <option key={opponent.id} value={opponent.id}>{opponent.scriptName}</option>);
9+
let opponents = this.props.opponents.map((opponent, index) => <option key={opponent.id} value={index}>{opponent.label}</option>);
10+
11+
let selectedIndex = this.props.opponents.findIndex((opponent) => (opponent.id == this.props.selectedOpponent.id && opponent.source == this.props.selectedOpponent.source));
1012

1113
return <Row>
1214
<Col sm={12}>
@@ -15,7 +17,7 @@ class LiveCodeSandboxSettingsTab extends React.Component {
1517
<form>
1618
<div className="form-group">
1719
<label htmlFor="opponent"><i className="fas fa-crosshairs"></i> Opponent</label>
18-
<select className="form-control" id="opponent" value={this.props.selectedOpponent} onChange={(e) => this.props.onOpponentChange(e.target.value)}>
20+
<select className="form-control" id="opponent" value={selectedIndex} onChange={(e) => this.props.onOpponentChange(this.props.opponents[e.target.value])}>
1921
{opponents}
2022
</select>
2123
</div>
@@ -45,7 +47,7 @@ class LiveCodeSandboxSettingsTab extends React.Component {
4547
}
4648

4749
LiveCodeSandboxSettingsTab.defaultProps = {
48-
selectedOpponent: '',
50+
selectedOpponent: {},
4951
opponents: [],
5052
mode: 'duel',
5153
rngSeed: 0,
@@ -57,7 +59,7 @@ LiveCodeSandboxSettingsTab.defaultProps = {
5759

5860
LiveCodeSandboxSettingsTab.propTypes = {
5961
opponents: PropTypes.array,
60-
selectedOpponent: PropTypes.string,
62+
selectedOpponent: PropTypes.object,
6163
rngSeed: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
6264
isRngLocked: PropTypes.bool,
6365
mode: PropTypes.oneOf(['duel', 'team']),

packages/jsbattle-webpage/src/components/test/LiveCodeSandboxSettingsTab.spec.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,32 @@ test('LiveCodeSandboxSettingsTab renders properly', () => {
1010

1111
test('list opponents', () => {
1212
const opponents = [
13-
{ id: 'op1', scriptName: 'alpha753' },
14-
{ id: 'op2', scriptName: 'beta78732' }
13+
{ id: 'op1', label: 'alpha753' },
14+
{ id: 'op2', label: 'beta78732' }
1515
]
1616
const wrapper = shallow(<LiveCodeSandboxSettingsTab
17-
selectedOpponent="op2"
17+
selectedOpponent={{ id: 'op2', label: 'beta78732' }}
1818
opponents={opponents}
1919
/>);
2020
expect(wrapper.find('#opponent').text()).toMatch(/alpha753/)
2121
expect(wrapper.find('#opponent').text()).toMatch(/beta78732/)
22-
expect(wrapper.find('#opponent').props().value).toBe('op2');
22+
expect(wrapper.find('#opponent').props().value).toBe(1);
2323
});
2424

2525
test('change opponent', () => {
2626
const opponents = [
27-
{ id: 'op532234', scriptName: 'alpha98324' },
28-
{ id: 'op632243', scriptName: 'beta77532' }
27+
{ id: 'op532234', label: 'alpha98324' },
28+
{ id: 'op632243', label: 'beta77532' }
2929
];
3030
const onOpponentChange = jest.fn();
3131
const wrapper = shallow(<LiveCodeSandboxSettingsTab
32-
selectedOpponent="op532234"
32+
selectedOpponent={{ id: 'op532234', label: 'alpha98324' }}
3333
opponents={opponents}
3434
onOpponentChange={onOpponentChange}
3535
/>);
36-
wrapper.find('#opponent').simulate('change', {target: { value : 'op632243'}});
36+
wrapper.find('#opponent').simulate('change', {target: { value : 1}});
3737
expect(onOpponentChange.mock.calls).toHaveLength(1);
38-
expect(onOpponentChange.mock.calls[0][0]).toBe('op632243');
38+
expect(onOpponentChange.mock.calls[0][0]).toHaveProperty('id', 'op632243');
3939
});
4040

4141
test('change mode', () => {
@@ -64,8 +64,8 @@ test('lock rng', () => {
6464
expect(onRngLock.mock.calls[0][0]).toBe(true);
6565

6666
onRngLock.mockReset();
67-
wrapper.find('#lock').simulate('change', {target: { checked : false}});
68-
expect(onRngLock.mock.calls).toHaveLength(1);
69-
expect(onRngLock.mock.calls[0][0]).toBe(false);
67+
wrapper.find('#lock').simulate('change', {target: { checked : false}});
68+
expect(onRngLock.mock.calls).toHaveLength(1);
69+
expect(onRngLock.mock.calls[0][0]).toBe(false);
7070

7171
});

packages/jsbattle-webpage/src/containers/SandboxScreen.js

+24-39
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
} from '../actions/statsAction.js';
1212
import {
1313
getAiScript,
14-
getSandboxAiScriptList,
14+
getSandboxOpponentList,
1515
updateAiScript,
1616
renameAiScript,
1717
setSandboxOpponent,
@@ -47,16 +47,17 @@ export class SandboxScreen extends React.Component {
4747
componentDidMount() {
4848
let id = this.props.match.params.name;
4949
this.props.getAiScript(id, this.props.useRemoteService);
50-
this.props.getSandboxAiScriptList(this.props.useRemoteService);
50+
this.props.getSandboxOpponentList(this.props.useRemoteService);
5151
this.props.notifySandboxEdit();
5252
this.log('SandboxScreen mounted');
5353
}
5454

5555
shouldComponentUpdate(nextProps) {
5656
if(!this.state.isRunning) return true;
57+
5758
if(
5859
this.props.mode != nextProps.mode ||
59-
this.props.opponent.type != nextProps.opponent.type ||
60+
this.props.opponent.source != nextProps.opponent.source ||
6061
this.props.opponent.name != nextProps.opponent.name
6162
) {
6263
this.updateAiDefList(nextProps.mode, nextProps.opponent);
@@ -71,13 +72,16 @@ export class SandboxScreen extends React.Component {
7172

7273
for(let i=0; i < count; i++) {
7374
let aiDef = JsBattle.createAiDefinition();
74-
switch(opponent.type) {
75+
switch(opponent.source) {
7576
case 'bundled':
7677
aiDef.fromFile(opponent.name);
7778
break;
78-
case 'user':
79+
case 'local_user':
80+
case 'remote_user':
7981
aiDef.fromCode(opponent.name, opponent.code);
8082
break;
83+
default:
84+
throw new Error(`Unsupported source: ${opponent.source}`);
8185
}
8286
aiDefList.push(aiDef);
8387
}
@@ -99,9 +103,8 @@ export class SandboxScreen extends React.Component {
99103
this.props.updateAiScript(this.props.script.id, code, this.props.useRemoteService);
100104
}
101105

102-
onOpponentChange(value) {
103-
value = value.split('/');
104-
this.props.setSandboxOpponent(value[0], value[1]);
106+
onOpponentChange(opponent) {
107+
this.props.setSandboxOpponent(opponent.source, opponent.id);
105108
}
106109

107110
onBattleFinish(result) {
@@ -133,35 +136,20 @@ export class SandboxScreen extends React.Component {
133136

134137
renderSettingsTab() {
135138
let selectedOpponent;
136-
if(this.props.opponent.type == 'user' && this.props.opponent.name == this.props.script.scriptName) {
137-
selectedOpponent = 'bundled/dummy';
139+
if((this.props.opponent.source == 'local_user' || this.props.opponent.source == 'remote_user') && this.props.opponent.name == this.props.script.scriptName) {
140+
selectedOpponent = {source: 'bundled', id: 'dummy'};
138141
} else {
139-
selectedOpponent = this.props.opponent.type + "/" + this.props.opponent.name;
142+
selectedOpponent = {source: this.props.opponent.source, id: this.props.opponent.id};
140143
}
141144

142-
let userTankList = this.props.userTankList
143-
.filter((script) => script.scriptName != this.props.script.scriptName)
144-
.map((row) => ({
145-
id: 'user/' + row.scriptName,
146-
scriptName: row.scriptName,
147-
bundled: false
148-
}));
149-
let bundledTankList = this.props.bundledTankList
150-
.map((name) => ({
151-
id: 'bundled/' + name,
152-
scriptName: 'Bundled: ' + name,
153-
bundled: true
154-
}));
155-
let opponents = bundledTankList.concat(userTankList);
156-
157145
return <LiveCodeSandboxSettingsTab
158146
rngSeed={this.state.rngSeed}
159147
isRngLocked={this.props.lockRng}
160148
mode={this.props.mode}
161-
opponents={opponents}
149+
opponents={this.props.opponentList}
162150
selectedOpponent={selectedOpponent}
163151
onBattleModeChange={(isTeam) => this.props.setSandboxBattleMode(isTeam)}
164-
onOpponentChange={(id) => this.onOpponentChange(id)}
152+
onOpponentChange={(opponent) => this.onOpponentChange(opponent)}
165153
onRngLock={(locked) => this.props.lockSandboxRng(locked)}
166154
/>;
167155
}
@@ -246,10 +234,9 @@ SandboxScreen.defaultProps = {
246234
logging: true,
247235
simQuality: 'auto',
248236
simSpeed: 1,
249-
userTankList: [],
250-
bundledTankList: [],
237+
opponentList: [],
251238
opponent: {
252-
type: 'user',
239+
source: 'local_user',
253240
name: "dummy",
254241
code: "importScripts('lib/tank.js'); tank.init(function(settings, info) { }); tank.loop(function(state, control) { });",
255242
},
@@ -266,7 +253,7 @@ SandboxScreen.defaultProps = {
266253
setSandboxBattleMode: () => {},
267254
lockSandboxRng: () => {},
268255
notifySandboxEdit: () => {},
269-
getSandboxAiScriptList: () => {},
256+
getSandboxOpponentList: () => {},
270257
};
271258

272259
SandboxScreen.propTypes = {
@@ -276,8 +263,7 @@ SandboxScreen.propTypes = {
276263
useRemoteService: PropTypes.bool,
277264
logging: PropTypes.bool,
278265
simSpeed: PropTypes.number,
279-
userTankList: PropTypes.array,
280-
bundledTankList: PropTypes.array,
266+
opponentList: PropTypes.array,
281267
opponent: PropTypes.object,
282268
script: PropTypes.object,
283269
mode: PropTypes.oneOf(['duel', 'team']),
@@ -288,16 +274,15 @@ SandboxScreen.propTypes = {
288274
setSandboxBattleMode: PropTypes.func,
289275
lockSandboxRng: PropTypes.func,
290276
notifySandboxEdit: PropTypes.func,
291-
getSandboxAiScriptList: PropTypes.func,
277+
getSandboxOpponentList: PropTypes.func,
292278
};
293279

294280
const mapStateToProps = (state) => ({
295281
isLoading: state.loading.AI_SCRIPT || state.loading.SANDBOX_AI_SCRIPT_LIST,
296282
isRenameLoading: state.loading.AI_SCRIPT_RENAME,
297283
simQuality: state.settings.simQuality,
298284
simSpeed: state.settings.simSpeed,
299-
userTankList: state.aiRepo.tankList,
300-
bundledTankList: state.sandbox.tankList,
285+
opponentList: state.sandbox.opponentList,
301286
opponent: state.sandbox.opponent,
302287
mode: state.sandbox.mode,
303288
lockRng: state.sandbox.lockRng,
@@ -309,8 +294,8 @@ const mapDispatchToProps = (dispatch) => ({
309294
getAiScript: (name, useRemoteService) => {
310295
dispatch(getAiScript(name, useRemoteService));
311296
},
312-
getSandboxAiScriptList: (useRemoteService) => {
313-
dispatch(getSandboxAiScriptList(useRemoteService));
297+
getSandboxOpponentList: (useRemoteService) => {
298+
dispatch(getSandboxOpponentList(useRemoteService));
314299
},
315300
updateAiScript: (id, code, useRemoteService) => {
316301
dispatch(updateAiScript(id, code, useRemoteService));

0 commit comments

Comments
 (0)