Loading...

Top
PFQ Banner

This is PokéFarm Q, a free online Pokémon collectables game.

Already a user? New to PFQ?

Single post in Dev Log 204

Forum Index > Core > Announcements > Dev Log 204 >

Niet [Adam]'s AvatarNiet [Adam]
Niet [Adam]'s Avatar
Oh... you guys are still here? Um... hi. Yeah, it's been a while, hasn't it? A year??? Guess I'd better have something good to show for it! Well, for starters, it's called PFNext now. In software development, when referring to versions, "next" refers to the upcoming, cutting edge development version of the thing, and I feel this is more appropriate a name than "new". "New" will eventually become "old", but "next" just becomes "stable". So from now on I'm calling it PFNext. Another thing that's changing is the name of the development server. Let's be honest, it was pure hubris to name the server arceus.pokefarm.com - it was just asking for trouble! Sure enough, the project was uncooperative. But from the pieces and lessons learned, I have created a new server, and this one feels appropriate to name suicune.pokefarm.com - not just for its association with Ho-oh and resurrection, but also for its abilities related to purifying. There will be no "dirty hacks" on PFNext, Suicune will make sure of it! Let's see, last time... I talked about restructuring the data files. Fixed table-like structure wasn't good enough for more complex data structures, and so I arranged them into JSON files instead, allowing them to be more human-readable (good for editing!) and still be machine-parseable. Let's put a pin in JSON handling, I'll come back to that. First, let's talk about the game engine itself. I think it goes without saying, but there isn't a pre-built engine for what I'm making. No Unity, Unreal or Godot, not even Game Maker will help here! PF needs a custom-built engine from the ground up, and I need to build that engine before I can actually make a game with it! While I can always tweak the engine in response to actual needs, designing a strong and stable base to work from is vital to the future success of the project. With that in mind, let's build an API! An API, or application programming interface, is a way for two computers to communicate with each other in a strongly-defined manner. In this case, the two computers are my server, and your browser. PFQ does not use an API in the strict sense. It technically is an API but it's not structured. There's no consistency in where the endpoints are, and some responses are "data for JavaScript to process" whereas others are "here's a block of HTML to display", and don't get me started on how the dialog boxes work... PFNext will be built around a well-defined and structured API. Requests will have a strictly-defined format, and responses will be stricter still. The standard I will be using is called OpenAPI, which is a kind of JSON Schema (hey, we've seen those before!) that defines everything about an API. Using this standard allows me to benefit from tools built to leverage the standard, in this case I am using Stoplight to render documentation and testing tools like so: (Click for fullsize) In this preview I called GET /api/v1/profile/u41C6AEE0 and got back an object containing information about my profile, "presence" (trainer card, forum signature etc.), party and - because it's my own profile - currency on my account. As of this writing, you can try it yourself, though availability may change in future. For now at least, feel free to have a poke around! So how does this work? Some requests, like the "GET profile" one above, are pretty straightforward. They might have a parameter (like profileID above), but otherwise they just retrieve information - which is sent in the form of JSON. But what if the browser needs to send data to the server? If you guessed JSON again, you get a cookie! Here's another example, this time of me reloading the Lab: (Click for fullsize) In this example, I performed the "refresh" action, along with "useReloader" set. The server responds with new Lab data, including the identity of the eggs available to me, along with "reloader_used" set to "false", indicating that a Lab Reloader was not needed because it had been long enough to be unnecessary. (Oops, that should be "reloaderUsed", brb...) All of this is defined in the specification, and structure of both input and output can be reviewed. So how does this work server-side? Because the browser can send anything to the server. What happens if you send invalid JSON? Or JSON that's missing certain parameters? Or has values out of range or of the wrong type? Let's get technical :D First, the incoming data is validated as JSON in the first place. If not, the server immediately responds with "400 Bad Request". So now we have a valid JSON object. Then, I define the expected structure of the JSON as a class. For example, the API to rename a Pokémon looks like this:
class
PatchParameters
extends
Parameters
{
/** ID of the Pokémon */
#[Identifier]
public int
$pokemonID
;
/** A new nickname for the Pokémon. May be set to an empty string to use the species name. */
#[Nullable(
new
SimpleString
(
Pokemon
::MAX_NAME_LENGTH)]
public
?
string
$nickname
;
/** Custom Description for the Pokémon. Supports "Basic Formatting". */
#[Nullable(
new
AnyString
(
Pokemon
::MAX_DESCRIPTION_LENGTH)]
public
?
string
$description
; }
Those bits like #[Identifier] are called "Attributes", and in this case they mark the properties below them with classes I have created called Validators. The "Identifier" validator, for instance, only accepts things that look like identifiers - that is some lowercase letters to determine the type of identifier, followed by an 8-digit uppercase Hex code. Attempting to send anything other than an Identifier to this property will cause the server to respond with a "400 Bad Request" response. Some attributes can be nested. For example the "Nullable" attribute accepts the property not existing, but if it does exist then it must match the nested validator. It can go further - the most complex one I have so far is this monstrositry:
#[ListOf(
new
Nullable
(
new
OneOf
(
new
Nested
(
Egg
::
class
),
new
Nested
(
Pokemon
::
class
))),
minItems
:
Party
::CAPACITY,
maxItems
:
Party
::CAPACITY)]
And yet if you just read it out loud, it tells you exactly what it is: a list of nullable (optional) things, either Eggs or Pokémon, with at least "party capacity" and at most "party capacity" items (in other words, exactly the party capacity!) - and from that you can probably figure out that this attribute represents the Party contents! These attributes are also used on the output properties. While in theory the server should know exactly what it's sending, setting attributes on them for validation provides two benefits: 1. It prevents errors by ensuring I haven't messed up code elsewhere 2. It allows clear documentation of what the output parameters are. Speaking of documentation, I wrote a script that reads out all of these structures and attributes to generate the documentation file for OpenAPI! It reads through all of the API files in my code, and thanks to their consistent and unified structure, it can read out the input/output parameters and their specifications, in order to generate the JSON file that makes the Stoplight tool render everything out. By having the documentation be automatically generated based on the code, a "contract" is formed: the documentation will always match what the code does. It isn't possible for the documentation to be wrong under this system! Which will be very helpful both for me, but also for... Plugin developers! You know how we have the Block User and the QoL userscripts on PFQ? Well, PFNext will expand this into an actual plugin system. Interested developers will be able to build a plugin, we'll have a system in place for registering plugins and publishing them for others to use. Site Skins will be a type of plugin - a very simple one that just has a colour definition file - but plugins can be as complex as desired, like the QoL one (although I suspect that one will have its features broken down into smaller, simpler plugins) and just overall will make customising your game experience much easier. Speaking from experience, good documentation is crucial for good plugin development, so hopefully all of the above will be very useful for those people. All of the benefits listed above are only possible with the creation of a well-designed and well-structured engine. So while I don't have much to show in terms of gameplay yet, I hope you can appreciate the value of the work I have been doing for the last year. Discuss!
Clip from Pokémon anime, re-lined by me
-- OMNOMNOM!
Featured story: Injustice Feedback welcome!
© PokéFarm 2009-2024 (Full details)Contact | Rules | Privacy | Reviews 4.6★Get shortlink for this page