Ovog sam proljeća naišao na projekt u kojem su dečki naučili kako pokrenuti Dota 2 poslužiteljsku verziju 2014. i, sukladno tome, igrati na njemu. Veliki sam obožavatelj ove igre i nisam mogao propustiti ovu jedinstvenu priliku da uronim u svoje djetinjstvo.
Zaronio sam vrlo duboko i dogodilo se da sam napisao Discord bota koji je odgovoran za gotovo sve funkcije koje nisu podržane u staroj verziji igre, naime matchmaking.
Prije svih inovacija s botom, predvorje je kreirano ručno. Prikupili smo 10 reakcija na poruku i ručno sastavili poslužitelj ili ugostili lokalni lobby.
Moja programerska priroda nije mogla izdržati toliki ručni rad i preko noći sam skicirao najjednostavniju verziju bota koji je automatski dizao server kad je bilo 10 ljudi.
Odmah sam odlučio pisati u nodejima, jer ne volim baš Python, a u ovakvom okruženju osjećam se ugodnije.
Ovo je moje prvo iskustvo pisanja bota za Discord, ali pokazalo se da je vrlo jednostavno. Službeni npm modul discord.js pruža prikladno sučelje za rad s porukama, prikupljanje reakcija itd.
Odricanje od odgovornosti: svi primjeri koda su "trenutni", što znači da su prošli kroz nekoliko iteracija prepisivanja noću.
Osnova povezivanja je "red" u koji se stavljaju igrači koji žele igrati i uklanjaju kada ne žele ili pronađu igru.
Ovako izgleda suština “igrača”. U početku je to bio samo korisnički ID u Discordu, ali postoje planovi za pokretanje/pretragu igara sa stranice, ali prvo na redu.
I ovdje je sučelje čekanja. Ovdje se umjesto "igrača" koristi apstrakcija u obliku "grupe". Za jednog igrača grupu čini on sam, a za igrače u grupi, odnosno svi igrači u grupi.
Odlučio sam koristiti događaje za razmjenu konteksta. Bilo je prikladno za slučajeve - nakon događaja "pronađena je igra za 10 ljudi", možete poslati potrebnu poruku igračima u privatnim porukama i provesti osnovnu poslovnu logiku - pokrenuti zadatak za provjeru spremnosti, pripremiti predvorje za lansiranje i tako dalje.
Za IOC koristim InversifyJS. Imam ugodno iskustvo rada s ovom knjižnicom. Brzo i jednostavno!
Imamo nekoliko redova čekanja na našem poslužitelju - dodali smo 1x1, normalno/ocijenjeno i nekoliko prilagođenih načina. Stoga postoji singleton RoomService koji se nalazi između korisnika i pretraživanja igre.
constructor(
@inject(GameServers) private gameServers: GameServers,
@inject(MatchStatsService) private stats: MatchStatsService,
@inject(PartyService) private partyService: PartyService
) {
super();
this.initQueue(MatchmakingMode.RANKED);
this.initQueue(MatchmakingMode.UNRANKED);
this.initQueue(MatchmakingMode.SOLOMID);
this.initQueue(MatchmakingMode.DIRETIDE);
this.initQueue(MatchmakingMode.GREEVILING);
this.partyService.addListener(
"party-update",
(event: PartyUpdatedEvent) => {
this.queues.forEach((q) => {
if (has(q.queue, (t) => t.is(event.party))) {
// if queue has this party, we re-add party
this.leaveQueue(event.qp, q.mode)
this.enterQueue(event.qp, q.mode)
}
});
}
);
this.partyService.addListener(
"party-removed",
(event: PartyUpdatedEvent) => {
this.queues.forEach((q) => {
if (has(q.queue, (t) => t.is(event.party))) {
// if queue has this party, we re-add party
q.remove(event.party)
}
});
}
);
}
(Kodirajte rezance da biste dobili ideju o tome kako procesi otprilike izgledaju)
Ovdje inicijaliziram red čekanja za svaki od implementiranih načina igre, a također osluškujem promjene u "grupama" kako bih prilagodio redove i izbjegao neke sukobe.
Dakle, dobro obavljeno, ubacio sam dijelove koda koji nemaju nikakve veze s temom, a sada prijeđimo izravno na spajanje.
Razmotrimo slučaj:
1) Korisnik želi igrati.
2) Da bi pokrenuo pretragu, koristi Gateway=Discord, odnosno postavlja reakciju na poruku:
3) Ovaj pristupnik ide na RoomService i kaže "Korisnik iz discorda želi ući u red čekanja, način: igra bez ocjene."
4) RoomService prihvaća zahtjev gatewaya i gura korisnika (točnije grupu korisnika) u željeni red čekanja.
5) Red čekanja provjerava svaki put kada ima dovoljno igrača za igru. Ako je moguće, emitirajte događaj:
6) RoomService očito radosno osluškuje svaki red u napetom iščekivanju ovog događaja. Kao ulaz primamo popis igrača, od njih formiramo virtualnu "sobu" i, naravno, izdajemo događaj:
queue.addListener("room-found", (event: RoomFoundEvent) => {
console.log(
`Room found mode: [${mode}]. Time to get free room for these guys`
);
const room = this.getFreeRoom(mode);
room.fill(event.players);
this.onRoomFormed(room);
});
7) Tako smo došli do “najvišeg” autoriteta - razreda Bot. Općenito, bavi se vezom između pristupnika (ne mogu shvatiti kako to smiješno izgleda na ruskom) i poslovne logike spajanja partnera. Bot čuje događaj i naređuje DiscordGatewayu da pošalje provjeru spremnosti svim korisnicima.
8) Ako netko odbije ili ne prihvati igru unutar 3 minute, NE VRAĆAMO ga u red čekanja. Sve ostale vraćamo u red i čekamo da opet bude 10 ljudi. Ako su svi igrači prihvatili igru, tada počinje zanimljiv dio.
Konfiguracija namjenskog poslužitelja
Naše se igre nalaze na VDS-u s Windows poslužiteljem 2012. Iz ovoga možemo izvući nekoliko zaključaka:
Na njemu nema dokera, što me pogodilo u srce
Štedimo na najmu
Zadatak je pokrenuti proces na VDS-u iz VPS-a na Linuxu. Napisao sam jednostavan poslužitelj u Flasku. Da, ne volim Python, ali što mogu? Brže je i lakše pisati ovaj poslužitelj na njemu.
Obavlja 3 funkcije:
Pokretanje poslužitelja s konfiguracijom - odabirom karte, broja igrača za pokretanje igre i skupa dodataka. Neću sad pisati o dodacima - to je druga priča s litrama kave noću pomiješanim sa suzama i počupanom kosom.
Zaustavljanje/ponovno pokretanje poslužitelja u slučaju neuspješnih veza, što možemo riješiti samo ručno.
Ovdje je sve jednostavno, primjeri koda nisu ni prikladni. skripta od 100 redaka
Dakle, kada se 10 ljudi okupilo i prihvatilo igru, server je pokrenut i svi su bili nestrpljivi da igraju, link za spajanje na igru je poslan u privatnim porukama.
Klikom na poveznicu igrač se spaja na server igre i to je to. Nakon ~25 minuta, virtualna "soba" s igračima je očišćena.
Unaprijed se ispričavam zbog nespretnosti članka, dugo nisam ovdje pisao, a previše je koda za isticanje važnih dijelova. Rezanci, ukratko.
Ako vidim interes za temu, bit će drugi dio - sadržavat će moje muke s dodacima za srcds (Source namjenski poslužitelj), i, vjerojatno, sustav ocjenjivanja i mini-dotabuff, mjesto sa statistikom igre.