Arms of War: Generals is written to have server-side ticks. What does that mean?
For an idle game, there are set intervals in which actions happen, colloquially dubbed 'ticks'. Each tick, some action is performed. This makes time as the dominant mechanic of almost all games, like a lot of energy systems. Ticks are usually triggered from your browser (also called client-side) and they tell the server that 'I am going to perform this action'. Others happen on the server (also called server-side) and they tell your browser that 'You've performed these actions'.
From a purely player-view, this doesn't mean much. But to the developer, it is massive difference. Client-side ticks are prone to hacking or attacks, where server-side ticks aren't as vulnerable to cheating, but to more conventional (and universally protected) forms of attack like denial of service attacks. Client-side ticks must implement additional measures to prevent players from cheating (e.g. I've performed 10 tasks in 1 second versus 1 task). It's an inherently vulnerable position to be in. To add to the complexity, you're locked in a perpetual arms race with your players where you, the developer, will always be on the losing side.
Okay, let's get into the meat of Generals and see what we can pick apart. I'm going to try to keep this as high-level and conceptual as possible, so if you get lost let me know so I can revise this part to be more clear.
When a match is started, an init function is ran to initialize all of the important parts of the match, ensure database continuity, and create the castles, players, and units to run the match. This also creates a setInterval to run the action ticks. This is the most efficient and manageable method I've found to iterate a process using Node (note: I did find Node timers recently, but haven't assessed their applicability yet). setInterval has a lot of problems with performance when there are a great many of them, so I decided to run all ticks on the same time schedule – once per second – thus giving only one setInterval being used instead of one per user. These ticks will process player actions, battles, end conditions, etc.
sidebar: I spoke with Vysn of Avabur about this, and he used setTimeouts for pretty much every aspect of his game, which seemed to not noticeably impact performance at all.
Each player who logs in will essentially ask the server what is happening. Upon receiving the data, their browser will:
- Reset the page according to the data received
- Begin mimicking what should be happening on the server with timers, etc
- Play along until the next refresh comes
This makes sure that the server is always the source of truth, and all players are operating on the same playing field within an acceptable margin. Any action a user does will trigger a manual and immediate data refresh.
- All player's actions are processed at once. Unless they're lucky enough to change state within a 5 milisecond timeframe, everyone gets to be on the same page all the time. Being a competitive game, ensuring everyone gets the same advantages is paramount.
- Lag proofing! Yep, if your internet connection can convey a 15 character message to AoW servers within 1,000 miliseconds (150 miliseconds is considered to be noticeable lag for most games), your actions will process at the same time as everyone else. By optimizing the length and frequency of messages sent back and forth, this effect can be increased even further.
- True offline ticks. A lot of idle games will try to 'crunch time' once you login to calculate what would have happened while you were gone. That is a great solution for a single player game, but for a realtime competitive game, other players need to see the field even when you log off. True offline ticks allow you to continue your progress while you're not logged in with 0 extra steps.
- Multiple access points. You can log in from your computer at home, from your phone while on-the-go, from your work computer (if the firewalls haven't caught on yet), and all devices will show the same information. As well, when you change your actions on one device, within 10 seconds it will update on the rest.
- A performance ceiling. Since each tick has to happen each second, the computations of the tick itself cannot exceed 1 second. If it does, then your server will slowly lag behind until the client and the server are completely desynchronized and chaos ensues. Optimizing what your ticking does can drastically help with this issue. As well, from initial testing from 30 players in the same match with highly unoptimized code, each tick took around 3ms to complete. Remember, Node is fast as fuck. Still, this is a limitation that cannot be mitigated entirely.
- Update deltas. If Generals depended on split-second decisions (which it really doesn't), there would be an issue involving how fast the clients are updated. Say player A logs in at 10 ms after each tick is processed, and player B logs in at 10 ms before each tick is processed. Player A is going to have an advantage to change tasks based on new information before player B knows about it, a whopping 980ms ahead of player B. This can be mitigated with decreasing the time that updates come into the client, but also has to be balanced with network performance (sending data every 10ms can be very taxing on modern computers).
Generals' design is half stumbled-upon accident and half intentional design, I'll admit. However, this portion is legitimately one of the best parts of the game architecture that sets it apart. No design is perfect, this certainly doesn't qualify. But this gives Generals some unique advantages over other PBBGs that can create a very different experience.