Skip to content

Commit

Permalink
Merge branch 'devel' of github.com:online-go/gtp2ogs into devel
Browse files Browse the repository at this point in the history
  • Loading branch information
anoek committed May 24, 2024
2 parents f1ed999 + b9e984c commit 9a4ff5f
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 23 deletions.
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ __pycache__
pip-log.txt

# Unit test / coverage reports
.coverage
.tox
nosetests.xml
.nyc_output
/coverage

# Translations
*.mo
Expand Down
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,31 @@ npm install -g gtp2ogs

### Building from source

To build from source you will need to have `node.js` installed on your system.
You will also need to have the `yarn` and `gulp` npm packages installed. Once
you have the prerequisites you can run `yarn` to install the package dependencies,
and

To build from source you will need to have node.js installed on your system.
You will also need to have `yarn` installed. Then, run
```
gulp
yarn install
yarn exec -- gulp
```

to run the build process. The resulting compiled javascript file will be located
in `dist/gtp2ogs.js` which you can then run with

```
node dist/gtp2ogs.js
```

To build a standalone binary that doesn't depend on node, use pkg:
```
yarn exec -- pkg -C brotli .
```

If you do not want to install node.js and yarn locally, another option is
```
docker run --rm -it -v "$PWD":/usr/src -w /usr/src node:slim yarn install
docker run --rm -it -v "$PWD":/usr/src -w /usr/src node:slim yarn exec -- gulp
docker run --rm -it -v "$PWD":/usr/src -w /usr/src node:slim yarn exec -- pkg -C brotli .
```

## Running your bot

Once you have your API Key and `gtp2ogs` installed, you can connect your bot to OGS
Expand Down
5 changes: 5 additions & 0 deletions example_config.json5
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,11 @@
*/
// verbosity: 0,

/** Enable logging game chat content.
* @default false
*/
// log_game_chat: true,

/** Sets how often the status lines are printed to the screen. Set to 0 to
* disable.
* units: milliseconds
Expand Down
5 changes: 5 additions & 0 deletions schema/Config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
"description": "Enable verbose logging.",
"default": 0
},
"log_game_chat": {
"type": "boolean",
"description": "Enable logging game chat.",
"default": false
},
"status_update_frequency": {
"type": "number",
"description": "Sets how often the status lines are printed to the screen. Set to 0 to disable. units: milliseconds",
Expand Down
52 changes: 46 additions & 6 deletions src/Game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class Game extends EventEmitter<Events> {
state: GoEngineConfig;
opponent_evenodd: null | number;
greeted: boolean;
startup_timestamp: number;
bot?: Bot;
using_opening_bot: boolean = false;
ending_bot?: Bot;
Expand Down Expand Up @@ -56,6 +57,7 @@ export class Game extends EventEmitter<Events> {
this.verbose = trace.debug.bind(null, `[game ${game_id}]`);
this.warn = trace.warn.bind(null, `[game ${game_id}]`);
this.error = trace.error.bind(null, `[game ${game_id}]`);
this.startup_timestamp = Date.now() / 1000;
this.state = null;
this.opponent_evenodd = null;
this.greeted = false;
Expand Down Expand Up @@ -226,6 +228,7 @@ export class Game extends EventEmitter<Events> {
// Try to connect again, to get the server to send the gamedata over.
socket.send("game/connect", {
game_id: game_id,
chat: config.log_game_chat,
});
return;
}
Expand Down Expand Up @@ -278,15 +281,17 @@ export class Game extends EventEmitter<Events> {
decodeMoves(move.move, this.state.width, this.state.height)[0],
this.state.width,
this.state.height,
this.my_color === "black" ? "white" : "black",
"black", // we are white so we are recording black's moves
),
);
}
if (this.ending_bot) {
ignore_promise(
this.ending_bot?.sendMove(
this.ending_bot.sendMove(
decodeMoves(move.move, this.state.width, this.state.height)[0],
this.state.width,
this.state.height,
this.my_color === "black" ? "white" : "black",
"black",
),
);
}
Expand All @@ -301,6 +306,7 @@ export class Game extends EventEmitter<Events> {
}
}
} else {
const opponent_color = this.my_color === "black" ? "white" : "black";
if (move.move_number % 2 === this.opponent_evenodd) {
// We just got a move from the opponent, so we can move immediately.
//
Expand All @@ -310,15 +316,17 @@ export class Game extends EventEmitter<Events> {
decodeMoves(move.move, this.state.width, this.state.height)[0],
this.state.width,
this.state.height,
this.my_color === "black" ? "white" : "black",
opponent_color,
),
);
}
if (this.ending_bot) {
ignore_promise(
this.ending_bot?.sendMove(
this.ending_bot.sendMove(
decodeMoves(move.move, this.state.width, this.state.height)[0],
this.state.width,
this.state.height,
this.my_color === "black" ? "white" : "black",
opponent_color,
),
);
}
Expand All @@ -336,8 +344,25 @@ export class Game extends EventEmitter<Events> {
socket.off(`game/${game_id}/move`, on_move);
});

if (config.log_game_chat) {
socket.send("chat/join", {
channel: `game-${game_id}`,
});
const on_chat = (d) => {
// Since there is no explicit tracking of which chats are
// "read", we assume anything from before we connected to the
// game has already been dealt with.
handleChatLine(game_id, d.line, this.startup_timestamp);
};
socket.on(`game/${game_id}/chat`, on_chat);
this.on("disconnecting", () => {
socket.off(`game/${game_id}/chat`, on_chat);
});
}

socket.send("game/connect", {
game_id: game_id,
chat: config.log_game_chat,
});

/*
Expand Down Expand Up @@ -822,6 +847,21 @@ export class Game extends EventEmitter<Events> {
}
}

export function handleChatLine(game_id: string, line: any, cutoff_timestamp: number) {
if (typeof line.body !== "string") {
return;
}
if (line.username === config.username) {
return;
}
// Both are UNIX epoch times.
if (line.date < cutoff_timestamp) {
return;
}

trace.info(`[game ${game_id}] Game chat from ${line.username}: ${line.body}`);
}

function num2char(num: number): string {
if (num === -1) {
return ".";
Expand Down
6 changes: 6 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export interface Config {
*/
verbosity?: number;

/** Enable logging game chat.
* @default false
*/
log_game_chat?: boolean;

/** Sets how often the status lines are printed to the screen. Set to 0 to
* disable.
* units: milliseconds
Expand Down Expand Up @@ -372,6 +377,7 @@ function defaults(): Config {
apikey: "",
server: "https://online-go.com",
verbosity: 1,
log_game_chat: false,
max_pause_time: 300,
status_update_frequency: 60000,
allowed_time_control_systems: ["fischer", "byoyomi", "simple"],
Expand Down
55 changes: 53 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { config, config_event_emitter, TimeControlRanges } from "./config";
import { socket } from "./socket";
import { trace } from "./trace";
import { post, api1 } from "./util";
import { Game } from "./Game";
import { Game, handleChatLine } from "./Game";
import { bot_pools } from "./pools";
import { JGOFTimeControl } from "goban/src/JGOF";
import { Speed } from "./types";
Expand Down Expand Up @@ -65,6 +65,7 @@ interface RejectionDetails {
class Main {
notification_connect_interval: ReturnType<typeof setInterval>;
connected_games: { [game_id: string]: Game };
connected_finished_games: { [game_id: string]: boolean };

//games_by_player: { [player_id: string]: Game[] };
connected: boolean;
Expand All @@ -77,6 +78,7 @@ class Main {

constructor() {
this.connected_games = {};
this.connected_finished_games = {};
//this.games_by_player = {}; // Keep track of connected games per player
this.connected = false;

Expand Down Expand Up @@ -329,6 +331,51 @@ class Main {
}
break;

case "lateChatReceivedInGame":
{
this.deleteNotification(notification);
if (!config.log_game_chat) {
break;
}
const game_id = notification.game_id;
if (game_id in this.connected_finished_games) {
// Already connected to the finished game.
break;
}

trace.debug(`Connecting to ${game_id} to receive late chats`);
socket.send("chat/join", {
channel: `game-${game_id}`,
});
const on_chat = (chat) => {
handleChatLine(game_id, chat.line, notification.timestamp - 1);
};
socket.on(`game/${game_id}/chat`, on_chat);

// Connecting to a game from outside Game deserves a little
// bit of care, but I think it should be OK, because
// lateChatReceivedInGame implies the game is over, so we
// should not be getting in the way of anything here.
//
// We could connect to the game as we usually do, but this
// would confuse the logic there that expects to handle an
// unfinished game.
this.connected_finished_games[game_id] = true;
socket.send("game/connect", {
game_id: game_id,
chat: true,
});
setTimeout(() => {
trace.debug(`Disconnecting from ${game_id} (chats)`);
delete this.connected_finished_games[game_id];
socket.send("game/disconnect", {
game_id: game_id,
});
socket.off(`game/${game_id}/chat`, on_chat);
}, 5000);
}
break;

default:
{
if (!(notification.type in ignorable_notifications)) {
Expand Down Expand Up @@ -710,7 +757,11 @@ class Main {
config;
if (config.max_games_per_player) {
const game_count = Object.keys(this.connected_games).filter((game_id) => {
return !!this.connected_games[game_id].state?.player_pool[player_id];
const state = this.connected_games[game_id]?.state;
if (state?.player_pool !== undefined) {
return !!state.player_pool[player_id];
}
return state?.white_player_id === player_id || state?.black_player_id === player_id;
}).length;
trace.log("Game count: ", game_count, " for ", player_id);
if (game_count >= config.max_games_per_player) {
Expand Down
19 changes: 15 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2595,13 +2595,14 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"

es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46:
version "0.10.62"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.62, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46:
version "0.10.64"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714"
integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==
dependencies:
es6-iterator "^2.0.3"
es6-symbol "^3.1.3"
esniff "^2.0.1"
next-tick "^1.1.0"

es6-iterator@^2.0.1, es6-iterator@^2.0.3:
Expand Down Expand Up @@ -2821,6 +2822,16 @@ eslint@8, eslint@^8.36.0, eslint@^8.7.0:
strip-json-comments "^3.1.0"
text-table "^0.2.0"

esniff@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308"
integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==
dependencies:
d "^1.0.1"
es5-ext "^0.10.62"
event-emitter "^0.3.5"
type "^2.7.2"

espree@^9.0.0, espree@^9.5.0:
version "9.5.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113"
Expand Down

0 comments on commit 9a4ff5f

Please sign in to comment.