Loading...

Top
PFQ Banner

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

Already a user? New to PFQ?

Dev Log 204

Forum Index > Core > Announcements >

Pages: 1234

Niet [Adam]'s AvatarNiet [Adam]
Niet [Adam]'s Avatar
Continuing the subject of "starting over (again)", I wanted to take the opportunity to have another look at the fundamentals of the game, and see what if any changes I want to make there. The biggest thing I know I want to change is to shift away from mass-whatever. PFNew will avoid features that require massive amounts of clicks, hatches, time or money, and instead be geared towards a more casual scale. Here's why: PFQ is intended to be an ongoing, endless experience. Some of you have been here for near to or even over a decade. The problem is that there's only so much content, and with the events that encourage mass activity, you're very quickly going to run out of content. This leaves just maintaining Arceus rank when new tournaments or MCWs come out, and Melan hunting as the last things to do (besides just hanging out and socialising - that's not really part of the game, it's just a cool thing you can do) and both of those features can be quite frustrating. Consequently this leads to veteran users, the most dedicated players, feeling the the game only has frustration to offer. Not good. PFNew will aim to be more relaxed overall. No scramble to get things done, no rush, just do what you want at your own pace. Sure there will be events that require some activity but nothing quite so crazy as we have here. Naturally, the rest of the game will be balanced accordingly. So one idea I've had recently is related to interactions. The original intent of interactions is to help each other progress by mutual boosting. However, on PFQ, the current meta is one of activating a Lucky Egg, finding users with large numbers of fields, and mass-clicking them. This does not help the recipient of the interactions since their stuff is already Lv100 anyway. It doesn't help anyone else since they're not part of the meta. And really it goes against the whole sense of community that was originally intended. The shift of focus towards party interactions has helped a little bit, but not nearly enough. On PFNew I'm thinking of outright removing interactions with field Pokémon. I have already shown in previous screenshots that the party page will have a single button to click to interact with everything in that party. This is how I want things to play out. You interact with the user, not their Pokémon. The consequence of interacting is still the same: their party members get EXP. But once you have interacted with them, you can't do so again until it resets. Even if they change their party. Naturally this will involve further balancing, so the base cooldown on interacting will be one hour, rather than resetting at midnight server time. This includes yourself! The goal of this is to make the most efficient option be to interact with more players, and in return get more clickbacks. Another major thing I want to look at is the sheer number of features PFQ has. Yes it's cool to have so many things you can do, but many features only have one real use. The PokéWalker doesn't even have any real uses because I didn't want to gate things behind a feature that not everyone can use. So for PFNew I want to take a look at those features and really ensure that they form a part of the game, rather than just being bolted on without care for why. One major example would be having some features provide EXP to the Pokémon you use in them, or items that can provide some benefit elsewhere in the game. I don't have any specific details right now but these are the kinds of things I'm thinking about. One more thing, I'm playing with ideas to make the Fakemon be more like regular Pokémon. Having our own custom region that they belong to. I don't know how I'm going to do that yet but it feels like it would be nice. Among other things this means that we can offer Taiveret, Flarbat and Hydrark as starter choices! Ultimately, the biggest thing to remember is that I'm not just remaking PFQ. I'm making a new game entirely. So keep in mind that these "changes" are intentionally drastic. These are changes that would never work on PFQ, but if PFNew is designed with them from the start and balanced accordingly, then all will be well. Discuss!
Clip from Pokémon anime, re-lined by me
-- OMNOMNOM!
Featured story: Injustice Feedback welcome!
Niet [Adam]'s AvatarNiet [Adam]
Niet [Adam]'s Avatar
"Have you tried turning it off and on again?" Sometimes, you just have to stop. Take a step back. Breathe. Ask yourself tough questions. "What am I doing here? What is it I want to achieve? What are my goals, and what will it take to reach them?" So it goes with PFQ/PFNew. What am I trying to accomplish? I have been struggling with this for some time now. Remember that PFNew's initial development started in 2018. Here we are coming up to five full years later and I have very little to show for it. I have ideas - loads of ideas - but not much has been done. Some of that is down to motivational issues, which I have been struggling with for a long time now, but another aspect is the technical side of things. I want to talk about those here. This post will get quite technical. I think it's interesting, but I'm a nerd so... yeah. There's a TL;DR at the end! PFQ is built using PHP and MySQL. Fairly standard for a website, but not really that great for something as complex as a game. I make it work, sure, and when the only tool you have is a hammer, every problem looks like a nail. That doesn't mean it's good. There are many things that very much could be improved, such as the load times of some pages, but not if I keep going with the same hammer. I'm considering different software architectures for PFNew. Lately I have discovered JSON Schema, and I'm very much enjoying it. JSON, short for JavaScript Object Notation, is a format for defining objects in a way that is easy for both humans and computers to read and understand. However, without additional context, these objects could be literally anything. That's where JSON Schema comes in. JSON Schema is basically "JSON that describes JSON". It uses a known vocabulary to define what the format of a given JSON file should be. (There's also Meta-Schemas, which are JSON Schemas that describe JSON Schemas!) The biggest advantage to JSON is that it allows more complex structures than a MySQL database. To give just one example, the base stats of a Pokémon on PFQ have to be stored as six separate "unsigned byte" columns, whereas in JSON they could form a single nested object with the six stats as properties. And there's no real limit to how deep this nesting can go, so long as the Schema permits it. To illustrate the massive difference this can have, let's talk about Evolution. And, of course, when Evolution comes up, you should immediately think of Eevee. Here's how Eevee's evolution data is stored on PFQ:
+-----+------+-----+-----------+ | id | from | to | condition | +-----+------+-----+-----------+ | 144 | 143 | 144 | I:16; | | 145 | 143 | 145 | I:14; | | 146 | 143 | 146 | I:15; | | 147 | 143 | 147 | H;D:1; | | 148 | 143 | 148 | H;D:0; | | 149 | 143 | 149 | F:grass; | | 150 | 143 | 150 | F:ice; | | 151 | 143 | 151 | E; | +-----+------+-----+-----------+
Does that make sense to you? I guess if you really focus you can kinda work out what it does. The first three are probably Vaporeon, Jolteon and Flareon, so that I:16; is most likely "evolves using an item", with the number being the item ID. "H" is clearly Happiness, with D for Daytime. F for Field, E for... affEction? Sure that makes sense. But only because I gave you the context that this is Eevee's evolution data. You'd have to know that Eevee is '143' in the database to start with! Here's the data again, but this time from the updated, JSON Schema-backed file:
"eevee"
: {
// ... (most species data here, omitted to focus on the evolutions)
"evolutions"
: [{
"target"
:
"vaporeon"
,
"conditions"
: {
"item"
:
"water-stone"
} },{
"target"
:
"jolteon"
,
"conditions"
: {
"item"
:
"thunder-stone"
} },{
"target"
:
"flareon"
,
"conditions"
: {
"item"
:
"fire-stone"
} },{
"target"
:
"espeon"
,
"conditions"
: {
"happiness"
:
true
,
"timeofday"
:
"day"
} },{
"target"
:
"umbreon"
,
"conditions"
: {
"happiness"
:
true
,
"timeofday"
:
"night"
} },{
"target"
:
"leafeon"
,
"conditions"
: {
"field"
:
"grass"
} },{
"target"
:
"glaceon"
,
"conditions"
: {
"field"
:
"ice"
} },{
"target"
:
"sylveon"
,
"conditions"
: {
"affection"
:
true
} }],
// ... some more data down here
}
Isn't that so much better to read? And understand? And work with? Of course... the big downside is that I actually have to translate PFQ's mess of data into this clean new format, but it's going well so far and it's allowing me the chance to rethink how some things are implemented. Such as, let's say, Shellos. On PFQ, Shellos hatches into either its East Sea or West Sea form depending on which side of your Party it is in when you hatch the egg. This is done by interrupting the Egg Hatch code with an additional hook, "if the species is Shellos, check its position in the party and swap its form if necessary". This is a very "imperative" style of programming, and while there's nothing wrong with that in theory, in practice this makes it very difficult to change or update things if necessary. This was fine originally, but as more Pokémon get added and more special cases are needed, it can become a real hassle to find where a particular case is actually handled. Contrast this to how it is handled in the new JSON format:
"shellos"
: {
// ... some data
"hatch_trigger"
: {
"type"
:
"east-west"
,
"east"
:
"east-sea"
,
"west"
:
"west-sea"
},
// ... more data
}
This more "declarative" style means that the "special case" is part of the species' data. There's still going to be a hook in the hatching code, but rather than specifically checking for Shellos, it will ask "does it have a hatch_trigger?" and perform some action if so. Other hatch triggers I've defined so far include "random-form" (for Unown that don't have Unown parents to pull inheritance from), "population" (for Basculin Red/Blue-Stripe) and "environmental-variation" (for Maravol, and as an evolution trigger for Vivillon). This makes things much easier to maintain and understand, which is a big problem that PFQ currently has. Speaking of evolution triggers, since they got casually mentioned above, those are handy for dealing with special cases too! Shedinja becomes a "spawn-clone" trigger with a few "overrides" to change its gender to Genderless and such. This means that if we ever wanted to add a Fakemon that has a Shedinja-like phantom, we totally can do that on PFNew without adding any further code - just declare its properties on its data. On top of that, I can make use of this new and flexible file format to simplify a lot of things. On PFQ, "Lefty Delphox" is actually handled as an entirely separate species, but with completely nulled-out data except for its name and sprite. That seems so wasteful! In the new file, it is simply an "alt-sprite" on the main species. Rewriting the data like this has also led to some great new ideas for PFNew as well: - Vivillon patterns can be made hereditary, allowing players to hunt all of the Shinies if they want to as they'll only need to collect a suitable parent of each pattern from other players. - Genesect will be able to use its held Drive as an alternative attack type in Spars thanks to a new "additional-type" property on its forms. - Formes in general are simplified into just one entry. For instance, Alolan Rattata is considered an alternative form of Rattata, with its breeding conditions declared and handled appropriately. All alternative forms count as a single Dex entry, but are still registered independently so that you can be as completionist as you want without needing every single form to complete the game. On that note, "Custom Sprites" can just be event-reward alternate forms as well! - Similarly, Unown will be a single Dex entry. As will Silvally, Arceus, and even Saiyan Rattata! Honestly this whole thing is just so much nicer to work with and I haven't even finished setting it up yet. I'm already loving it, and the ideas that it has led to. Nidoran will be able to have its different stats for males and females again! If we decide to add actual movesets for some more advanced battling system then Meowstic will be able to have its gender-specific movesets too. It's all there, all defined in this file! Of course this is only really possible because features are being planned out in advance, unlike PFQ which really just grew like slime mould. (If you haven't seen slime mould growth patterns, by the way, they actually look quite impressive, if a little gross!). Features like Fusion, which didn't exist when PFQ was first developed, can be formally introduced as data elements. The Kyurem involved could then have a "fusion partner" item in its data to point to the Reshiram/Zekrom it is fused with. For Calyrex this would provide a much cleaner way to handle its various Rider forms that PFQ has (eg. Shiny Calyrex riding an Albino Spectrier). TL;DR I'm changing the format that data is stored in for PFNew, from a rigid table-like structure to a flexible object-based one. This will massively simplify how the Pokédex works on PFNew, while allowing a greater flexibility in how players choose to approach 'dex completion. Exciting times! My motivation is flowing at last, and although this does mean starting PFNew over again, this should be the one that actually takes off, because it is being planned properly. I hope. XD Discuss!
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!

Pages: 1234

Cannot post: Please log in to post

© PokéFarm 2009-2024 (Full details)Contact | Rules | Privacy | Reviews 4.6★Get shortlink for this page