Note: This blog has been previously published on the Acorel Blog.
Warning: This blog post is going to be techy, but also fun. I will show how I realized an idea that has been in my head for quite some time: creating a multiplayer game in ABAP. Who wouldn’t want to play games with his/her colleagues while your boss thinks you’re very busy working in SAP?
In this blog post we will create an ABAP version of good old Tic-tac-toe, where two users can compete for eternal fame.
To make it more interesting we’ll teach the old ABAP dog a new trick: Instead of using a clumsy refresh button, the opponent will automatically be notified when the other player has made a move using a technique called long-polling.
The end result
First of all, have a look at the end result:
Why create a game in ABAP?
First of all: It’s fun.
Secondly, it is always good to see how far you can push the technology you’re using. The technique demonstrated here can also be applied to more serious applications, such as collaboration tasks or working with near-realtime information.
Even though the traditional SAP Gui isn’t the major SAP UI that is was, the mechanism shown applies to any UI technology and can also be applied in Web Dynpro, the Web UI, HTML5, etc.
How does it work?
Note: I won’t deal with the basics of a tic-tac-toe game, these are easy enough to figure out and if you’re interested, just ask in the comments or take a look at the supplied ABAP source code.
Normally, in SAP Gui (but also a lot of other UI technology) the client will trigger a refresh to see if there is new data. This is a called Pull mechanism and it is ok for most data-driven applications. Not for collaboration tasks and near realtime communication.
The following figure shows how a Pull mechnanism works:
We have a SAP Netweaver ABAP server and two SAP Gui clients that both compete in the same game. As said, there is no real mechanism to push an event or data from the ABAP stack to a SAP Gui.
This means that the while the first player is making a move, the seconds player will have no way to know when it is his turn. Therefore, either a refresh button or a refresh at a fixed interval is needed for the game to progress.
Long-polling to the rescue
The following figure shows how long polling works:
The left figure shows how a traditional pull system would work. The client (SAP Gui) would periodically initiate a refresh of data from the server to find out wether the other player has made a move yet. This will lead to a lot of unneeded data transfer and also a poor user experience: The user will have to press the refresh button a lot or have to endure a lot screen flicker while the Gui is polling for updates.
The figure on the right shows long polling. In this situation, the client (SAP Gui) is still iniating data transfer, but rather than answering the query directly, the server is going to delay until the desired situation is true: the other player has made a move, it is now your turn.
Note that all the while, the connection stays open. The connection can stay open indefinetely or or just a while longer than the expected time needed for a single move.
To make it more concrete, these are the ABAP objects used to create the game:
The UI for the user is in the ABAP program ZTICTACTOE, which consists of a few simple screens. Data is stored in two tables:
- ZTICTACTOELOBBY is used in matchmaking. When a user starts the program, he is entered in the lobby and matched to an already waiting user. If there are no other players the user will wait until another player enters the lobby.
- ZTICTACTOEGAME stores the gamestate: the state of the board, the current player etc.
The lock object EZTICTACTOE is what controls the long polling. The player that has to make a move has acquired a SAP lock on the game (as identified by the ID of the ZTICTACTOEGAME table). While that player is making a move, the ZTICTACTOE session of the other player will try to acquire the lock every second until he succeeds. If this is the case, the screen will be refreshed and the other player can make a move.
Show me the code
No blog post is complete without some source code:
The Function Module ZCHECK_GAME_LOCK tries to acquire a lock on the game, and will perform a while loop until it is successfull or it will give up after 60 seconds (MAX_SECONDS):
* for up to MAX_SECONDS, check if anyone has a lock on the given game WHILE sy-uzeit <= lv_end. WAIT UP TO 1 SECONDS. CLEAR: lt_enq. CALL FUNCTION 'ENQUEUE_READ'; EXPORTING gclient = sy-mandt gname = 'ZTICTACTOEGAME'; garg = lv_argument guname = ''; TABLES enq = lt_enq EXCEPTIONS communication_failure = 1 system_failure = 2 OTHERS = 3. CHECK sy-subrc = 0 AND lt_enq IS INITIAL. RETURN. ENDWHILE.
The report will call Function Module Z_CHECK_GAME_MATCH in an asynchronous way:
CALL FUNCTION 'ZCHECK_GAME_LOCK'; STARTING NEW TASK 'WAITING'; DESTINATION 'NONE'; PERFORMING return_from_wait ON END OF TASK EXPORTING game_id = gs_game-game_id EXCEPTIONS communication_failure = 1 system_failure = 2.
When the Function Module is finished (either it is the players turn or 60 seconds have passed), the return_from_wait routine is executed:
FORM return_from_ wait USING taskname. SET USER-COMMAND 'REFRESH'. ENDFORM.
This routine simply refreshes the screen. Two outomces are possible:
- If it is now the players turn, the UI will change accordingly and the user is allowed to make a move.
- If 60 seconds have passed and the other player still didn’t make a move, the program simply calls the ZCHECK_GAME_LOCK Function Module again.
You’ll need the following plugins to import the nugget: