Skip to content
This repository has been archived by the owner on Nov 15, 2020. It is now read-only.

Port to new log format #260

Open
wants to merge 10 commits into
base: persistent-server
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import "./event_types";

export { Address } from './networking/EventWire';
export { BotRunner, BotConfig } from './planetwars/BotRunner';
export { PwClient } from './planetwars/PwClient';
export { Replayer } from "./replay";
export { Reactor } from "./reactors/Reactor";
export { ServerRunner, ServerParams } from "./planetwars/ServerRunner";
export { Logger } from "./Logger";
export { Event, SimpleEventEmitter } from "./reactors/SimpleEventEmitter"
export { PwMatch } from "./planetwars/PwMatch"

import * as events from "./eventTypes";
import * as PwTypes from './planetwars/PwTypes';

export { events };
export { PwTypes };
34 changes: 14 additions & 20 deletions client/src/replay.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createReadStream } from "fs";
import { createReadStream, ReadStream } from "fs";
import { ProtobufReader } from "./networking/ProtobufStream";
import * as protocol_root from './proto';
import { SimpleEventEmitter, EventType } from "./reactors/SimpleEventEmitter";
import { SimpleEventDispatcher } from 'ste-simple-events';
import LogEvent = protocol_root.mozaic.log.LogEvent;
import * as events from './eventTypes';
import { ISimpleEvent } from "ste-simple-events";
Expand All @@ -12,6 +13,7 @@ import { ISimpleEvent } from "ste-simple-events";

export class Replayer {
emitters: {[clientId: number]: SimpleEventEmitter};
public clientSpottedDispatcher: SimpleEventDispatcher<number> = new SimpleEventDispatcher<number>();

constructor() {
this.emitters = {};
Expand All @@ -22,6 +24,7 @@ export class Replayer {
if (!emitter) {
emitter = new SimpleEventEmitter();
this.emitters[clientId] = emitter;
this.clientSpottedDispatcher.dispatch(clientId);
}
return emitter;
}
Expand All @@ -31,33 +34,24 @@ export class Replayer {
}

private emit(logEvent: LogEvent) {
const emitter = this.emitters[logEvent.clientId];
if (emitter) {
emitter.handleWireEvent({
typeId: logEvent.eventType,
data: logEvent.data,
});
}
const emitter = this.clientStream(logEvent.clientId);
emitter.handleWireEvent({
typeId: logEvent.eventType,
data: logEvent.data,
});
}

public replayFile(path: string) {
const logStream = createReadStream(path);
this.replayReadStream(logStream);
}

public replayReadStream(logStream: ReadStream) {
const messageStream = logStream.pipe(new ProtobufReader());

messageStream.on('data', (bytes: Uint8Array) => {
const logEvent = LogEvent.decode(bytes);
this.emit(logEvent);
});
}
}

const replayer = new Replayer();

// just print all events for now
Object.keys(events).forEach((eventName) => {
replayer.on(events[eventName]).subscribe((event) => {
console.log(event);
});
});

replayer.replayFile('log.out');
}
2 changes: 1 addition & 1 deletion planetwars/client/app/actions/matches/hosting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ export const changeBotSlot = actionCreator<M.BotSlot>('CHANGE_BOT_SLOT');
export const selectMap = actionCreator<string>('SELECT_MAP');
export const playerConnected = actionCreator<M.Token>('PLAYER_CONNECT');
export const playerDisconnected = actionCreator<M.Token>('PLAYER_DISCONNECT');
export const serverStarted = actionCreator<PwClient.MatchRunner>('SERVER_STARTED');
export const serverStarted = actionCreator<PwClient.Reactor>('SERVER_STARTED');
78 changes: 42 additions & 36 deletions planetwars/client/app/actions/matches/matches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Config } from '../../utils/Config';
import * as Notify from '../notifications';
import * as Host from './hosting';
import { actionCreator } from '../helpers';
import { createWriteStream } from 'fs';

export const importMatchFromDB = actionCreator<M.Match>('IMPORT_MATCH_FROM_DB');
export const importMatchError = actionCreator<string>('IMPORT_MATCH_ERROR');
Expand All @@ -37,7 +38,7 @@ function createHostedMatch(params: M.MatchParams): M.HostedMatch {
return match;
}

export function joinMatch(host: M.Address, bot: M.InternalBotSlot) {
export function joinMatch(address: M.Address, bot: M.InternalBotSlot) {
return (dispatch: any, getState: any) => {
const state: GState = getState();

Expand All @@ -48,7 +49,7 @@ export function joinMatch(host: M.Address, bot: M.InternalBotSlot) {
type: M.MatchType.joined,
status: M.MatchStatus.playing,
timestamp: new Date(),
network: host,
network: address,
logPath: Config.matchLogPath(matchId),
bot,
};
Expand All @@ -57,36 +58,36 @@ export function joinMatch(host: M.Address, bot: M.InternalBotSlot) {

const [command, ...args] = stringArgv(state.bots[bot.botId].command);
const botConfig = { command, args };
const address = host;
const number = 1;
const token = new Buffer(bot.token, 'hex');
const connectionData = { token, address: host };
const logger = new PwClient.Logger(match.logPath);
const clientParams = { token, address, logger, number, botConfig };

PwClient.Client.connect({
host: address.host,
port: address.port,
token: new Buffer(bot.token, 'hex'),
logger: new PwClient.Logger(match.logPath),
}).then((client) => {
const pwClient = new PwClient.PwClient(client, botConfig);
pwClient.onExit.subscribe(() => {
const host = address.host;
const port = address.port;
const token = Buffer.from(bot.token, 'utf-8');
const clientParams = {
token,
host,
port,
botConfig,
clientId: bot.clientid,
logSink: createWriteStream(match.logPath),
};
try {
const pwClient = new PwClient.PwClient(clientParams);
pwClient.on(PwClient.events.GameFinished).subscribe(() => {
dispatch(completeMatch(match.uuid));
const title = 'Match ended';
const body = `A remote match has ended`;
const link = `/matches/${match.uuid}`;
dispatch(Notify.addNotification({ title, body, link, type: 'Finished' }));
});
}).catch((error) => {
console.log(error);
dispatch(handleMatchError(match.uuid, error));
pwClient.run();
} catch (err) {
console.log(err);
dispatch(handleMatchError(match.uuid, err));
const title = 'Match errored';
const body = `A remote match on map has errored`;
const link = `/matches/${match.uuid}`;
dispatch(Notify.addNotification({ title, body, link, type: 'Error' }));
});
};
}
}
}

// https://github.com/ZeusWPI/MOZAIC/blob/1f9ab238e96028e3306bfe6b27920f70f9fba430/client/src/test.ts#L38
Expand All @@ -97,13 +98,13 @@ export function sendGo() {
if (!matchParams) { throw Error('Under construction'); }

const config = {
max_turns: matchParams.maxTurns,
map_file: state.maps[matchParams.map].mapPath,
maxTurns: matchParams.maxTurns,
mapPath: state.maps[matchParams.map].mapPath,
};

if (runner) {
console.log("running");
runner.matchControl.startGame(config);
runner.dispatch(PwClient.events.StartGame.create(config));
}
};
}
Expand Down Expand Up @@ -140,39 +141,44 @@ export function runMatch() {
};
});

const config: PwClient.MatchParams = {
const config: PwClient.ServerParams = {
address: params.address,
logFile: match.logPath,
ctrl_token: params.ctrl_token,
};

console.log("This probably doesn't work!!!!");

PwClient.MatchRunner.create(Config.matchRunner, config).then((runner) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this no longer a Promise? (don't have a real setup here to easily check myself)

try {
const server = new PwClient.ServerRunner(Config.matchRunner, config);
server.runServer();

const runner = new PwClient.Reactor(new PwClient.Logger(0, createWriteStream(match.logPath)));


dispatch(Host.serverStarted(runner));

runner.matchControl.onPlayerConnected.subscribe((clientId) => {
dispatch(Host.playerConnected(players[clientId - 1].token));
runner.on(PwClient.events.ClientConnected).subscribe((event) => {
dispatch(Host.playerConnected(players[event.clientId - 1].token));
});
runner.matchControl.onPlayerDisconnected.subscribe((clientId) => {
dispatch(Host.playerDisconnected(players[clientId - 1].token));
runner.on(PwClient.events.ClientDisconnected).subscribe((event) => {
dispatch(Host.playerDisconnected(players[event.clientId - 1].token));
});
runner.onComplete.subscribe(() => {
server.onExit.subscribe(() => {
dispatch(completeMatch(match.uuid));
const title = 'Match ended';
const body = `A match on map '${state.maps[params.map].name}' has ended`;
const link = `/matches/${match.uuid}`;
dispatch(Notify.addNotification({ title, body, link, type: 'Finished' }));
});
})
.catch((error) => {
} catch (error) {
dispatch(handleMatchError(match.uuid, error));
const title = 'Match errored';
const body = `A match on map '${state.maps[params.map].name}' has errored`;
const link = `/matches/${match.uuid}`;
dispatch(Notify.addNotification({ title, body, link, type: 'Error' }));
});
};
}
}
}

export function completeMatch(matchId: M.MatchId) {
Expand Down
28 changes: 21 additions & 7 deletions planetwars/client/app/components/join/Join.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface ImportCopy {
name: string;
host: string;
port: number;
clientId: number;
}

export interface JoinState {
Expand All @@ -32,6 +33,7 @@ export interface JoinState {
token: string;
lastClipboard: string;
import?: ImportCopy;
clientId?: number;
}

export class Join extends React.Component<JoinProps, JoinState> {
Expand All @@ -47,6 +49,7 @@ export class Join extends React.Component<JoinProps, JoinState> {
};
this.setAddress = this.setAddress.bind(this);
this.setToken = this.setToken.bind(this);
this.setClientid = this.setClientid.bind(this);
this.setBotId = this.setBotId.bind(this);
this.joinGame = this.joinGame.bind(this);
}
Expand All @@ -63,6 +66,10 @@ export class Join extends React.Component<JoinProps, JoinState> {
<input type="text" onChange={this.setToken} value={this.state.token}/>
</HorizontalInput>

<HorizontalInput id="clientid" label="Client ID">
<input type="number" onChange={this.setClientid} value={this.state.clientId}/>
</HorizontalInput>

<BotSelector
bots={this.props.allBots}
value={this.state.botId}
Expand All @@ -84,7 +91,7 @@ export class Join extends React.Component<JoinProps, JoinState> {
<a className={"button is-link"} onClick={this.importConfig}>
Import "{this.state.import.name}" from clipboard
</a>
</div> ):
</div> ) :
undefined
}
</div>
Expand All @@ -95,17 +102,17 @@ export class Join extends React.Component<JoinProps, JoinState> {

private importConfig = () => {
if (!this.state.import) { return; }
const {host, port, name, token} = this.state.import;
this.setState({ address: {host, port}, token });
const {host, port, name, token, clientId } = this.state.import;
this.setState({ address: {host, port}, token, clientId });
}

private checkClipboard = () => {
const clipBoardtext = clipboard.readText();
if (this.state.lastClipboard !== clipBoardtext) {
try {
const {host, port, name, token} = JSON.parse(clipBoardtext);
if (host && port && name && token) {
this.setState({ import: { host, port, name, token } });
const {host, port, name, token, clientId } = JSON.parse(clipBoardtext);
if (host && port && name && token && clientId) {
this.setState({ import: { host, port, name, token, clientId } });
} else {
this.setState({ import: undefined });
}
Expand All @@ -118,7 +125,7 @@ export class Join extends React.Component<JoinProps, JoinState> {
}

private isValid() {
return this.state.botId && this.state.token;
return this.state.botId && this.state.token && this.state.clientId;
}

private setAddress(address: M.Address) {
Expand All @@ -133,6 +140,12 @@ export class Join extends React.Component<JoinProps, JoinState> {
});
}

private setClientid(evt: React.FormEvent<HTMLInputElement>) {
this.setState({
clientId: parseInt(evt.currentTarget.value, 10),
});
}

private setBotId(botId: M.BotId) {
this.setState({ botId });
}
Expand All @@ -144,6 +157,7 @@ export class Join extends React.Component<JoinProps, JoinState> {
botId: this.state.botId!,
name: this.props.allBots[this.state.botId!].name,
connected: true,
clientid: this.state.clientId!,
};
this.props.joinMatch(this.state.address, bot);
}
Expand Down
11 changes: 7 additions & 4 deletions planetwars/client/app/components/matches/LogView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
MatchLog,
GameState,
Player,
PwTypes,
} from '../../lib/match';

import * as classNames from 'classnames';
Expand Down Expand Up @@ -118,6 +117,9 @@ export const PlayerTurnView: SFC<{ turn: PlayerTurn }> = ({ turn }) => {
case 'commands': {
return <CommandsView commands={action.value}/>;
}
default: {
return <div>"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa"</div>;
iasoon marked this conversation as resolved.
Show resolved Hide resolved
}
}
};

Expand All @@ -143,9 +145,10 @@ export const ParseErrorView: SFC<ParseErrorViewProps> = (props) => {
);
};

export interface CommandsViewProps { commands: PwTypes.PlayerCommand[]; }
// TODO: typing
export interface CommandsViewProps { commands: any/*PlayerCommand[]*/; }
export const CommandsView: SFC<CommandsViewProps> = (props) => {
const dispatches = props.commands.map((cmd, idx) => {
const dispatches = props.commands.map((cmd: any , idx: number) => {
const isWarning = { [styles.warning]: !!cmd.error };
return (
<li className={classNames(styles.playerOutput, isWarning)} key={idx}>
Expand All @@ -171,7 +174,7 @@ export const DispatchError: SFC<{ error?: string }> = ({ error }) => {
}
};

export const DispatchView: SFC<{ cmd: PwTypes.Command }> = ({ cmd }) => {
export const DispatchView: SFC<{ cmd: any /*Command*/ }> = ({ cmd }) => {
const { ship_count, origin, destination } = cmd;
return (
<div className={styles.dispatch}>
Expand Down
Loading