Skip to content

Commit b860a07

Browse files
committed
Finished WS client.
1 parent 55b1c3e commit b860a07

File tree

4 files changed

+181
-41
lines changed

4 files changed

+181
-41
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Logs
22
logs
3+
log
34
*.log
45
npm-debug.log*
56
yarn-debug.log*

node/client.js

Lines changed: 168 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ const axios = require('axios').default;
2626
const Logger = require('./log');
2727
const Judge = require('./index');
2828

29+
const languageFileExension = {
30+
'cpp98': 'cpp', 'cpp03': 'cpp', 'cpp11': 'cpp', 'cpp14': 'cpp', 'cpp17': 'cpp', 'cpp20': 'cpp',
31+
'c99': 'c', 'c11': 'c', 'c17': 'c',
32+
'nodejs14': 'js',
33+
'scratch3': 'sb3', 'clipcc3': 'ccproj'
34+
};
35+
2936
class JudgeClient {
3037
constructor() {
3138
this.ws = null;
@@ -35,6 +42,11 @@ class JudgeClient {
3542

3643
const configPath = 'h2oj-judge.yml';
3744
this.config = yaml.parse(fs.readFileSync(configPath, { encoding: 'utf-8' }));
45+
46+
this.createDirectoryWithCheck(path.join(this.config.cache_dir), true);
47+
this.createDirectoryWithCheck(path.join(this.config.cache_dir, 'problem'));
48+
this.createDirectoryWithCheck(path.join(this.config.cache_dir, 'judge'));
49+
this.createDirectoryWithCheck(path.join(this.config.cache_dir, 'checker'));
3850

3951
this.axios = axios.create({
4052
baseURL: this.config.server_url,
@@ -46,6 +58,11 @@ class JudgeClient {
4658
});
4759
}
4860

61+
sendJSON(data) {
62+
this.logger.log('Data sended: ', JSON.stringify(data));
63+
return this.ws.send(JSON.stringify(data));
64+
}
65+
4966
resetAxios() {
5067
this.axios = axios.create({
5168
baseURL: this.config.server_url,
@@ -58,6 +75,16 @@ class JudgeClient {
5875
});
5976
}
6077

78+
getTime() {
79+
return Math.floor(Number(new Date()) / 1000);
80+
}
81+
82+
createDirectoryWithCheck(path, recursive = false) {
83+
if (!fs.existsSync(path)) {
84+
fs.mkdirSync(path, { recursive: recursive });
85+
}
86+
}
87+
6188
async connectServer() {
6289
const res = await this.axios.post('judge/verify', {
6390
token: this.config.client_token
@@ -72,16 +99,32 @@ class JudgeClient {
7299
console.log(wsURL);
73100
this.ws = new WebSocket(wsURL);
74101
this.ws.on('open', () => {
75-
this.logger.log('on open');
76-
this.ws.send(JSON.stringify({
102+
this.logger.log('Websocket opened.');
103+
this.sendJSON({
77104
token: this.config.client_token
78-
}));
105+
});
106+
setInterval(() => {
107+
this.sendJSON({
108+
event: 'ping',
109+
time: this.getTime()
110+
});
111+
}, 15000);
79112
});
80113
this.ws.on('message', msg => {
81114
this.logger.log('Data Received: ', msg.toString());
82115
const data = JSON.parse(msg);
83116
if (data.event === 'judge') {
84-
this.judge(data.data);
117+
this.judge(data.data, status => {
118+
this.sendJSON({
119+
event: 'status',
120+
data: status
121+
});
122+
}, result => {
123+
this.sendJSON({
124+
event: 'end',
125+
data: result
126+
});
127+
});
85128
}
86129
});
87130
this.ws.on('close', code => {
@@ -90,29 +133,43 @@ class JudgeClient {
90133
this.ws.on('error', error => {
91134
this.logger.err('Websocket error: ', error);
92135
});
93-
/*await new Promise((resolve) => {
94-
this.ws.once('open', async () => {
95-
if (!this.config.noStatus) {
96-
const info = await sysinfo.get();
97-
this.ws.send(JSON.stringify({ key: 'status', info }));
98-
setInterval(async () => {
99-
const [mid, inf] = await sysinfo.update();
100-
this.ws.send(JSON.stringify({ key: 'status', info: { mid, ...inf } }));
101-
}, 1200000);
102-
}
103-
resolve(null);
104-
});
105-
});*/
106136
this.logger.log('Connected.');
107137
}
108138

109-
async judge(data) {
110-
await this.checkDataCache(data.problem_id.toString());
139+
setupWorkingDirectory(dir_path, wk = true) {
140+
this.createDirectoryWithCheck(dir_path);
141+
if (wk) this.createDirectoryWithCheck(path.join(dir_path, 'working'));
142+
this.createDirectoryWithCheck(path.join(dir_path, 'binary'));
143+
this.createDirectoryWithCheck(path.join(dir_path, 'source'));
144+
}
145+
146+
async judge(data, callback, finish) {
147+
const submissionId = data.submission_id.toString();
148+
const problemId = data.problem_id.toString();
149+
this.setupWorkingDirectory(path.join(this.config.cache_dir, 'judge', submissionId));
150+
await this.checkDataCache(problemId);
151+
const fileName = await this.fetchSource(submissionId);
152+
const checkerId = await this.checkChecker(problemId);
153+
154+
Judge.judge({
155+
sandboxDirectory: this.config.sandbox_dir,
156+
workingDirectory: path.join(this.config.cache_dir, 'judge', submissionId),
157+
problemDirectory: path.join(this.config.cache_dir, 'problem', problemId),
158+
checkerPath: path.join(this.config.cache_dir, 'checker', checkerId, 'binary/checker'),
159+
sourceName: fileName,
160+
type: data.language
161+
}, data => {
162+
this.logger.log('Judge testcase [', data.id, ']:\n', JSON.stringify(data));
163+
callback(data);
164+
}).then(result => {
165+
finish(result);
166+
});
111167
}
112168

113169
async fetchData(problemId) {
114170
this.logger.log('Fetching data: ', problemId);
115-
const filePath = path.join(this.config.cache_dir, problemId);
171+
const filePath = path.join(this.config.cache_dir, 'problem', problemId);
172+
this.createDirectoryWithCheck(filePath);
116173
const file = fs.createWriteStream(path.join(filePath, 'data.zip'));
117174
const res = await this.axios.get('judge/get_data', {
118175
responseType: 'stream',
@@ -127,23 +184,103 @@ class JudgeClient {
127184
});
128185
}
129186

187+
async fetchSource(submissionId) {
188+
this.logger.log('Fetching src: ', submissionId);
189+
const filePath = path.join(this.config.cache_dir, 'judge', submissionId, 'source');
190+
191+
const res = await this.axios.get('judge/get_source', {
192+
responseType: 'stream',
193+
params: {
194+
submission_id: submissionId
195+
}
196+
});
197+
let fileName = decodeURI(RegExp("filename=\"([^;]+\\.[^\\.;]+)\";*").exec(res.headers['content-disposition'])[1]);
198+
console.log(fileName);
199+
const file = fs.createWriteStream(path.join(filePath, fileName));
200+
res.data.pipe(file);
201+
return fileName;
202+
}
203+
204+
readIntFromFile(fileName) {
205+
try {
206+
return Number(fs.readFileSync(fileName, { encoding: 'utf-8' }));
207+
}
208+
catch (err) {
209+
return 0;
210+
}
211+
}
212+
213+
writeIntToFile(fileName, value) {
214+
fs.writeFileSync(fileName, value.toString(), { encoding: 'utf-8' });
215+
}
216+
130217
async checkDataCache(problemId) {
131-
const cacheDir = path.join(this.config.cache_dir, problemId);
132-
if (!fs.existsSync(cacheDir)) {
133-
fs.mkdirSync(cacheDir, { recursive: true });
134-
fs.writeFileSync(path.join(cacheDir, 'last_sync'), '0');
218+
const problemCache = path.join(this.config.cache_dir, 'problem', problemId);
219+
let lastSync = 0;
220+
const lastUpdate = (await this.axios.get('judge/check_data', {
221+
params: { problem_id: problemId }
222+
})).data.data.last_update;
223+
if (!fs.existsSync(problemCache)) {
224+
fs.mkdirSync(problemCache);
225+
}
226+
else {
227+
lastSync = this.readIntFromFile(path.join(problemCache, 'last_sync'));
228+
}
229+
this.logger.log('Data update time: ', lastUpdate, '/', lastSync);
230+
if (lastUpdate > lastSync) {
135231
await this.fetchData(problemId);
232+
this.writeIntToFile(path.join(problemCache, 'last_sync'), lastUpdate);
233+
}
234+
}
235+
236+
async checkChecker(problemId) {
237+
const problemCache = path.join(this.config.cache_dir, 'problem', problemId);
238+
const problemConfig = yaml.parse(fs.readFileSync(path.join(problemCache, 'config.yml'), { encoding: 'utf-8' }));
239+
let checkerId = problemId;
240+
if (problemConfig.mode != 'spj') {
241+
checkerId = problemConfig.mode;
242+
}
243+
244+
const checkerDir = path.join(this.config.cache_dir, 'checker', checkerId);
245+
let lastCompile = 0;
246+
const lastSync = this.readIntFromFile(path.join(problemCache, 'last_sync'));
247+
if (!fs.existsSync(checkerDir)) {
248+
fs.mkdirSync(checkerDir);
136249
}
137250
else {
138-
const lastSync = Number(fs.readFileSync(path.join(cacheDir, 'last_sync'), { encoding: 'utf-8' }));
139-
const lastUpdate = (await this.axios.get('judge/check_data', {
140-
params: { problem_id: problemId }
141-
})).data.data.last_update;
142-
this.logger.log('Data update time: ', lastUpdate, '/', lastSync);
143-
if (lastUpdate > lastSync) {
144-
await this.fetchData(problemId);
251+
lastCompile = this.readIntFromFile(path.join(checkerDir, 'last_compile'));
252+
}
253+
this.logger.log('Checker update time: ', lastCompile, '/', lastSync);
254+
if (lastSync > lastCompile) {
255+
if (problemId === checkerId) {
256+
await this.compileChecher(checkerId, false);
257+
}
258+
else {
259+
await this.compileChecher(checkerId, true);
145260
}
261+
this.writeIntToFile(path.join(checkerDir, 'last_compile'), lastSync);
146262
}
263+
return checkerId;
264+
}
265+
266+
async compileChecher(checkerId, builtin) {
267+
let result;
268+
this.setupWorkingDirectory(path.join(this.config.cache_dir, 'checker', checkerId), false);
269+
const srcPath = path.join(this.config.cache_dir, 'checker', checkerId, 'source', 'checker.cpp');
270+
if (builtin) {
271+
fs.copyFileSync(path.join(this.config.builtin_checker, checkerId + '.cpp'), srcPath);
272+
}
273+
else {
274+
fs.copyFileSync(path.join(this.config.cache_dir, 'problem', checkerId, 'checker.cpp'), srcPath);
275+
}
276+
result = await Judge.compileChecker({
277+
sandboxDirectory: this.config.sandbox_dir,
278+
workingDirectory: path.join(this.config.cache_dir, 'checker', checkerId),
279+
sourceName: 'checker.cpp',
280+
type: 'testlib'
281+
});
282+
console.log(result);
283+
return result;
147284
}
148285
}
149286

node/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ function compileChecker(config) {
9191
'--compilechecker'
9292
], async (error, _stdout, _stderr) => {
9393
if (error) reject(error);
94+
console.log(_stdout);
95+
console.log(_stderr);
9496
const resultPath = path.join(config.workingDirectory, 'result.yml');
9597
const result = yaml.parse(fs.readFileSync(resultPath, { encoding: 'utf-8' }));
9698
resolve(result);

src/main.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ int judge_program(argparse::ArgumentParser &parser) {
9393
return -1;
9494
}
9595

96-
const fs::path judger_config_path = "./hoj-judger-config.yml";
96+
const fs::path judger_config_path = "./h2oj-judge.yml";
9797
const fs::path problem_path = parser.get<std::string>("problem");
9898
const fs::path problem_config_path = problem_path / "config.yml";
9999
const fs::path work_path = parser.get<std::string>("workdir");
@@ -337,16 +337,16 @@ int judge_program(argparse::ArgumentParser &parser) {
337337
break;
338338
}
339339
}
340-
341-
// Output status
342-
std::cout << i + 1 << ','
343-
<< judge_result["case"][i]["detail"] << ','
344-
<< judge_result["case"][i]["score"] << ','
345-
<< judge_result["case"][i]["status"] << ','
346-
<< judge_result["case"][i]["time"] << ','
347-
<< judge_result["case"][i]["memory"] << std::endl;
348340
}
349341
}
342+
343+
// Output status
344+
std::cout << i + 1 << ','
345+
<< judge_result["case"][i]["detail"] << ','
346+
<< judge_result["case"][i]["score"] << ','
347+
<< judge_result["case"][i]["status"] << ','
348+
<< judge_result["case"][i]["time"] << ','
349+
<< judge_result["case"][i]["memory"] << std::endl;
350350
}
351351

352352
judge_result["score"] = total_score;
@@ -361,7 +361,7 @@ int judge_program(argparse::ArgumentParser &parser) {
361361
}
362362

363363
int compile_checker(argparse::ArgumentParser &parser) {
364-
const fs::path judger_config_path = "./hoj-judger-config.yml";
364+
const fs::path judger_config_path = "./h2oj-judge.yml";
365365
const fs::path work_path = parser.get<std::string>("workdir");
366366
const fs::path source_directory = work_path / "source";
367367
const fs::path binary_directory = work_path / "binary";

0 commit comments

Comments
 (0)