254 lines
6.5 KiB
JavaScript
254 lines
6.5 KiB
JavaScript
import { socket } from "./main.js";
|
|
import { Packet } from "./packet.js";
|
|
|
|
const REGIONS = {};
|
|
|
|
class Continent {
|
|
constructor(name) {
|
|
this.name = name;
|
|
this.yield = 0;
|
|
}
|
|
}
|
|
|
|
export class Strength {
|
|
constructor(cipherText, regionName) {
|
|
this.cipherText = cipherText;
|
|
this.assumedStrength = null;
|
|
|
|
this.prover = null;
|
|
document.addEventListener("PROOF", (ev) => {
|
|
const data = ev.detail;
|
|
|
|
if (
|
|
data.region === regionName &&
|
|
data.stage === "CHALLENGE" &&
|
|
this.prover !== null
|
|
) {
|
|
let z = this.prover.prove(BigInt(data.challenge));
|
|
|
|
socket.emit(
|
|
"message",
|
|
Packet.createProof(regionName, "0x" + z.toString(16))
|
|
);
|
|
}
|
|
});
|
|
|
|
this.verifier = null;
|
|
document.addEventListener("PROOF", (ev) => {
|
|
const data = ev.detail;
|
|
|
|
if (
|
|
data.region === regionName &&
|
|
data.stage === "PROOF" &&
|
|
this.verifier !== null
|
|
) {
|
|
let result = this.verifier.verify(BigInt(data.z));
|
|
if (result > 0) {
|
|
this.assumedStrength = this.verifier.plainText;
|
|
document.dispatchEvent(new CustomEvent("updateStrengths"));
|
|
} else {
|
|
console.warn(`Failed to verify ciphertext! ${result}`);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
transparentUpdate(value) {
|
|
this.cipherText.update(new Ciphertext(this.cipherText.pubKey, value, 0n));
|
|
|
|
if (this.assumedStrength !== null) {
|
|
this.assumedStrength += value;
|
|
}
|
|
}
|
|
|
|
update(cipherText) {
|
|
if (this.cipherText === null) {
|
|
this.cipherText = cipherText;
|
|
} else {
|
|
this.cipherText.update(cipherText);
|
|
}
|
|
|
|
this.assumedStrength = null;
|
|
}
|
|
|
|
prove(region) {
|
|
if (this.cipherText.readOnly) {
|
|
return;
|
|
}
|
|
|
|
this.prover = this.cipherText.prove();
|
|
|
|
socket.emit(
|
|
"message",
|
|
Packet.createProofConjecture(
|
|
region,
|
|
"0x" + this.cipherText.plainText.toString(),
|
|
"0x" + this.prover.a.toString(16)
|
|
)
|
|
);
|
|
}
|
|
|
|
verify(region, plainText, a) {
|
|
if (!this.cipherText.readOnly) {
|
|
return;
|
|
}
|
|
|
|
this.verifier = this.cipherText.prove(plainText, a);
|
|
|
|
socket.emit(
|
|
"message",
|
|
Packet.createProofChallenge(
|
|
region,
|
|
"0x" + this.verifier.challenge.toString(16)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
export class Region {
|
|
constructor(name, continent) {
|
|
this.name = name;
|
|
this.owner = null;
|
|
this.strength = new Strength(null, name);
|
|
this.neighbours = new Set();
|
|
this.continent = continent;
|
|
|
|
this.attackResolver = null;
|
|
this.attackerRes = null;
|
|
this.defenderRes = null;
|
|
|
|
REGIONS[name] = this;
|
|
}
|
|
|
|
static setNeighbours(region1, region2) {
|
|
region1.neighbours.add(region2);
|
|
region2.neighbours.add(region1);
|
|
}
|
|
|
|
static allRegionsClaimed() {
|
|
return (
|
|
Object.values(REGIONS).find((region) => region.owner === null) === undefined
|
|
);
|
|
}
|
|
|
|
static getRegion(name) {
|
|
return REGIONS[name];
|
|
}
|
|
|
|
static getAllRegions() {
|
|
return Object.values(REGIONS);
|
|
}
|
|
|
|
claim(player, cipherText) {
|
|
this.owner = player;
|
|
this.strength.update(cipherText);
|
|
this.strength.assumedStrength = 1n;
|
|
}
|
|
|
|
reinforce(cipherText) {
|
|
this.strength.update(cipherText);
|
|
}
|
|
|
|
isClaimed() {
|
|
return this.strength.cipherText !== null;
|
|
}
|
|
|
|
displayStrength() {
|
|
if (this.owner === null) {
|
|
return "";
|
|
} else if (!this.strength.cipherText.readOnly) {
|
|
return this.strength.cipherText.plainText.toString();
|
|
} else if (this.strength.assumedStrength !== null) {
|
|
return this.strength.assumedStrength.toString();
|
|
} else {
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
prove() {
|
|
this.strength.prove(this.name);
|
|
}
|
|
|
|
requestProof() {
|
|
socket.emit("message", Packet.createProofRequest(this.name));
|
|
}
|
|
|
|
verify(plainText, a) {
|
|
this.strength.verify(this.name, plainText, a);
|
|
}
|
|
|
|
async handleResolve(resolution) {
|
|
await navigator.locks.request(`region-${this.name}`, () => {
|
|
if (resolution.author === this.owner.id) {
|
|
this.defenderRes = resolution;
|
|
} else {
|
|
this.attackerRes = resolution;
|
|
}
|
|
|
|
if (
|
|
this.attackResolver !== null &&
|
|
this.defenderRes !== null &&
|
|
this.attackerRes !== null
|
|
) {
|
|
this.attackResolver({
|
|
attackerRes: this.attackerRes,
|
|
defenderRes: this.defenderRes,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
async resolveAttack() {
|
|
let promise;
|
|
await navigator.locks.request(`region-${this.name}`, () => {
|
|
if (this.attackerRes === null || this.defenderRes === null) {
|
|
let resolver;
|
|
promise = new Promise((resolve) => {
|
|
resolver = resolve;
|
|
});
|
|
this.attackResolver = resolver;
|
|
} else {
|
|
promise = new Promise((resolve) => {
|
|
resolve({
|
|
attackerRes: this.attackerRes,
|
|
defenderRes: this.defenderRes,
|
|
});
|
|
});
|
|
}
|
|
});
|
|
return promise;
|
|
}
|
|
}
|
|
|
|
window.Region = Region;
|
|
|
|
const EAST = new Continent("East");
|
|
const WEST = new Continent("West");
|
|
|
|
const A = new Region("A", EAST);
|
|
const B = new Region("B", EAST);
|
|
const C = new Region("C", EAST);
|
|
const D = new Region("D", EAST);
|
|
const J = new Region("J", EAST);
|
|
|
|
const F = new Region("F", WEST);
|
|
const G = new Region("G", WEST);
|
|
const H = new Region("H", WEST);
|
|
const I = new Region("I", WEST);
|
|
const E = new Region("E", WEST);
|
|
|
|
Region.setNeighbours(A, B);
|
|
Region.setNeighbours(A, C);
|
|
Region.setNeighbours(B, C);
|
|
Region.setNeighbours(B, J);
|
|
Region.setNeighbours(C, D);
|
|
Region.setNeighbours(C, F);
|
|
Region.setNeighbours(E, J);
|
|
Region.setNeighbours(E, I);
|
|
Region.setNeighbours(E, H);
|
|
Region.setNeighbours(F, J);
|
|
Region.setNeighbours(F, G);
|
|
Region.setNeighbours(G, H);
|
|
Region.setNeighbours(G, I);
|
|
Region.setNeighbours(H, I);
|