In 2015, while developing a browser-based online game, we faced the following task: to make a second server with different settings and game mechanics, and also to lead the future development of a new version of the game, which will have even more changes, with a common framework for all versions of the game same. The matter is complicated by the fact that the game requires constant updates and fixes, because gameplay goes and stop it because of the development of a parallel version can not be.
We use SVN as a version control system, and the theory says that in our case it is reasonable to make branches (branch), and then slyly merge them. Well, or do fork the product in a new repository, and develop a new version separately. But we went our own way, which is damn convenient, and may be useful to readers, about him and my story.
Step 1. We take a static table in the database (state), where different server state parameters are stored, for example, the game date, and add the server_type field
Step 2. We do statically methods that will determine the type of server
')
public static boolean isRoundServerType() { return serverType == SERVER_TYPE_ROUND || serverType == SERVER_TYPE_EVOLUTION; } public static boolean isEvolutionServerType() { return serverType == SERVER_TYPE_EVOLUTION; }
Step 3. Preparing methods for the web interface to determine the type of server:
public boolean getRoundServer() { return State.isRoundServerType(); } public boolean getEvolutionServer() { return State.isEvolutionServerType(); }
Step 4. In all places of business logic we insert blocks of the type:
public String[] getProcessTimes() { if (State.isEvolutionServerType()) { String[] times = { "09.00", "11.00", "13.00", "15.00", "17.00", "19.00", "21.00" }; return times; } else if (State.isRoundServerType()) { String[] times = { "08.00", "10.00", "12.00", "14.00", "16.00", "18.00", "20.00" }; return times; } else { String[] times = { "09.30", "11.30", "13.30", "15.30", "17.30", "19.30", "22.30" }; return times; } }
Step 5. In all places of the web interface we insert the check for accessibility of the interface element.
<item name="corporationtotal" caption=" " href="/corporation/controltotal/" hide="game.evolutionServer"/> <item name="citytotal" caption=" " href="/corporation/citycontrol/" show="game.evolutionServer"/>
Here it is necessary to make an amendment to the fact that we use a self-written framework that allows you to hide elements by two types of rules: show / hide - where the value of which field is directly calculated, allow / deny - then the user who is logged in is asked for rights to some actions . The last rule makes it possible to develop directly in the main repository branch, simply hiding the new functionality with rights for example:
<button icon="adddoc" caption=" " action="javascript:dialog('/intercitynew/')" allow="admin:gamemanager"/>
By developing the topic further, you can do the same way split testing mechanics in different parts of the game world by simply replacing the implementation of the old logic with a new one.
if (RetailFormula.isEvolutionRetail(currentCity.getId().toInteger())) { house = new House4(row, date); } else { house = new House2(row, date); }
A kind of dependency injection, but there are undeniable advantages due to the flexible and transparent variant of the algorithm settings.
The code turns out to be a bit plump, but it has minimal costs for supporting the entire system, since in fact, 1 flag in the database to change the logic of the server. Experience has shown that such coexistence of versions does not cause any particular problems, and promising developments are noticeably accelerated.