{"id":2857,"date":"2022-07-15T13:48:19","date_gmt":"2022-07-15T17:48:19","guid":{"rendered":"https:\/\/karstenrutledge.com\/?post_type=article&#038;p=2857"},"modified":"2022-07-15T13:48:42","modified_gmt":"2022-07-15T17:48:42","slug":"k-r-engineering-example-server-script-full-perm-v2-0-0","status":"publish","type":"article","link":"https:\/\/karstenrutledge.com\/index.php\/article\/k-r-engineering-example-server-script-full-perm-v2-0-0\/","title":{"rendered":"K.R. Engineering Example Server Script (Full Perm) v2.0.0"},"content":{"rendered":"<p>\/\/ K.R. Engineering Prize Server Sample Script for Content Creators (full perm)<br \/>\n\/\/ Written by Karsten Rutledge<br \/>\n\/\/ March 3rd, 2017<\/p>\n<p>\/\/ This script is intended to serve both as a framework and as documentation on how to create 3rd-party objects that process and react to game events from K.R. Engineering games.<\/p>\n<p>\/\/ This script is designed to work with Gaming.SL LIVE enabled games that support the new API Framework.<\/p>\n<p>\/\/ For additional information about the prize server API, please visit our Support Wiki at http:\/\/wiki.karstenrutledge.com\/<\/p>\n<p>\/\/ Default runtime state.<br \/>\ndefault {<br \/>\n\/\/ Incoming messages from other scripts. Specifically from the Prize Server API script which should also be in the contents of the same object that this script is in.<br \/>\nlink_message(integer sender, integer num, string message, key data) {<br \/>\n\/\/ Only respond to &#8220;api event&#8221; messages.<br \/>\nif (message == &#8220;api event&#8221;) {<br \/>\n\/\/ All API events are in the format of:<br \/>\n\/\/ data = game_uuid|game_name|game_model|game_version|game_license|API|api_event|event_specific_information|&#8230;|&#8230;|&#8230;<\/p>\n<p>\/\/ First turn the blob of data into a usable list variable that we can index.<br \/>\nlist api_event = llParseStringKeepNulls((string)data, [&#8220;|&#8221;], []);<\/p>\n<p>\/\/ Now break the list up into usable variables.<br \/>\n\/\/ These variables are consistent across all API event types. Other variables can be created based on the specific event type.<br \/>\nkey game_uuid = (key)llList2String(api_event, 0);<br \/>\nstring game_name = llList2String(api_event, 1);<br \/>\n\/\/ ^^^^ &#8220;Greedy Greedy&#8221;, &#8220;Simopolis&#8221;, etc&#8230;<br \/>\nstring game_model = llList2String(api_event, 2);<br \/>\n\/\/ ^^^^ As of the time of this writing, the only game model is &#8220;Standard&#8221;, but there may be more in the future.<br \/>\nstring game_version = llList2String(api_event, 3);<br \/>\n\/\/ ^^^^ We&#8217;re using a string type for version because it is in the format of MAJOR.MINOR.MAINT such as 3.2.12 which is not a float type.<br \/>\nkey game_license = (key)llList2String(api_event, 4);<br \/>\n\/\/ We skip over api_event[5] because that is always just &#8220;API&#8221;.<br \/>\nstring event_type = llList2String(api_event, 6);<br \/>\n\/\/ =============================<\/p>\n<p>\/\/ If\/elseif structure to determine behaviour based on event_type.<br \/>\nif (event_type == &#8220;LOADED&#8221;) {<br \/>\n\/\/ Game just loaded a new license.<br \/>\n} else if (event_type == &#8220;UNLOADED&#8221;) {<br \/>\n\/\/ Game just unloaded a license (currently unlicensed)<br \/>\n} else if (event_type == &#8220;SHUTDOWN&#8221;) {<br \/>\n\/\/ Game was shutdown (rezzer turned off)<br \/>\n} else if (event_type == &#8220;STATE&#8221;) {<br \/>\n\/\/ Game state has changed.<\/p>\n<p>\/\/ Set up event-specific variables.<br \/>\nstring game_state = llList2String(api_event, 7);<br \/>\n\/\/ ==========================<\/p>\n<p>if (game_state == &#8220;RESET&#8221;) {<br \/>\n\/\/ Game was reset.<br \/>\n} else if (game_state == &#8220;STARTED&#8221;) {<br \/>\n\/\/ A new game has started.<br \/>\n} else if (game_state == &#8220;ENDED&#8221;) {<br \/>\n\/\/ A game is over. We will probably be getting a WON API event following this, dealt with below.<br \/>\n\/\/ This is not for winning stuff. This is just to say that the game is over and done.<br \/>\n}<br \/>\n} else if (event_type == &#8220;PLAYER JOIN&#8221;) {<br \/>\n\/\/ A player has joined the game.<\/p>\n<p>\/\/ Set up event-specific variables.<br \/>\ninteger player_index = llList2Integer(api_event, 7);<br \/>\n\/\/ ^^^^ player_index is the position they joined the game. If they sat in the first chair, they are &#8220;player 1&#8221;, if they sat in the second chair, they are &#8220;player 2&#8221;, etc.<br \/>\nkey player_uuid = (key)llList2String(api_event, 8);<br \/>\n\/\/ ==========================<\/p>\n<p>\/\/ Keep track of player data here.<br \/>\n} else if (event_type == &#8220;PLAYER LEFT&#8221;) {<br \/>\n\/\/ A player has left the game.<\/p>\n<p>\/\/ Set up event-specific variables.<br \/>\ninteger player_index = llList2Integer(api_event, 7);<br \/>\n\/\/ ^^^^ player_index is the position they occupied prior to leaving the game. If they sat in the first chair, they are &#8220;player 1&#8221;, if they sat in the second chair, they are &#8220;player 2&#8221;, etc.<br \/>\nkey player_uuid = (key)llList2String(api_event, 8);<br \/>\n\/\/ ==========================<\/p>\n<p>\/\/ Clean up player data here.<br \/>\n} else if (event_type == &#8220;PLAYER SCORE&#8221;) {<br \/>\n\/\/ A player&#8217;s score has changed, for better or worse.<\/p>\n<p>\/\/ Set up event-specific variables.<br \/>\ninteger player_index = llList2Integer(api_event, 7);<br \/>\n\/\/ ^^^^ player_index is the position they occupied prior to leaving the game. If they sat in the first chair, they are &#8220;player 1&#8221;, if they sat in the second chair, they are &#8220;player 2&#8221;, etc.<br \/>\nkey player_uuid = (key)llList2String(api_event, 8);<br \/>\ninteger player_score = llList2Integer(api_event, 9);<br \/>\n\/\/ ==========================<\/p>\n<p>\/\/ Score change stuff here.<br \/>\n\/\/ In the case of team games, you should receive a separate score change event for each player on the team.<br \/>\n} else if (event_type == &#8220;ACHIEVEMENT&#8221;) {<br \/>\n\/\/ A player was awarded an achievement.<\/p>\n<p>\/\/ Set up event-specific variables.<br \/>\nkey player_uuid = (key)llList2String(api_event, 7);<br \/>\nstring player_name = llList2String(api_event, 8);<br \/>\nstring achievement_name = llList2String(api_event, 9);<br \/>\n\/\/ ==========================<\/p>\n<p>\/\/ Hooray, achievement?<br \/>\n} else if (event_type == &#8220;TURN&#8221;) {<br \/>\n\/\/ The turn has changed.<\/p>\n<p>\/\/ Set up event-specific variables.<br \/>\ninteger player_turn = llList2Integer(api_event, 7);<br \/>\n\/\/ ==========================<\/p>\n<p>if (player_turn) {<br \/>\n\/\/ It is Player (player_turn)&#8217;s turn.<br \/>\n\/\/ Do stuff that should be triggered by turn changes here.<br \/>\n} else {<br \/>\n\/\/ A false player turn (player_turn == 0) means that it is currently NOBODY&#8217;S TURN.<br \/>\n\/\/ This probably means nobody is sitting at the game currently, or the game is otherwise waiting for something to happen.<br \/>\n}<br \/>\n} else if (event_type == &#8220;ROUND&#8221;) {<br \/>\n\/\/ The round has advanced.<br \/>\n\/\/ Not all games have &#8217;rounds&#8217;.<\/p>\n<p>\/\/ Set up event-specific variables.<br \/>\ninteger game_round = llList2Integer(api_event, 7);<br \/>\n\/\/ ==========================<\/p>\n<p>\/\/ Do stuff that should be triggered on round changes.<br \/>\n\/\/ This event, if applicable, happens when one round has ended and a new one has started.<br \/>\n\/\/ The idea of what is a &#8220;round&#8221; varies from game to game. For some games it simply means all players have taken a turn and come back<br \/>\n\/\/ to the first player again (Greedy Greedy). For other games, it may mean a specific criteria has been reached to terminate the end of a round (Hearts for example, meaning all cards have been played).<br \/>\n} else if (event_type == &#8220;EVENT&#8221;) {<br \/>\n\/\/ Amusingly, EVENT is also an event type.<br \/>\n\/\/ The EVENT event happens when something happens to a player in the game.<br \/>\n\/\/ Super descriptive, right?<br \/>\n\/\/ Basically this is a way for the game signal that something good or bad happened to a player, in various degrees of severity.<br \/>\n\/\/ What these events are and what happens during them is entirely up to the game in question, but we try to provide you with enough information here<br \/>\n\/\/ that you could have generic reactions to &#8220;good&#8221; or &#8220;bad&#8221; game events, or process specific events for specific games if you wish.<\/p>\n<p>\/\/ **************<br \/>\n\/\/ For lists of game-specific events that can be sent, please visit our Support Wiki at http:\/\/wiki.karstenrutledge.com\/<br \/>\n\/\/ **************<\/p>\n<p>\/\/ Set up event-specific variables.<br \/>\n\/\/ player index|player uuid|type|severity|subtype|subtype_specific_details|&#8230;|&#8230;|&#8230;<br \/>\ninteger player_index = llList2Integer(api_event, 7);<br \/>\n\/\/ ^^^^ player_index is the position they occupied prior to leaving the game. If they sat in the first chair, they are &#8220;player 1&#8221;, if they sat in the second chair, they are &#8220;player 2&#8221;, etc.<br \/>\nkey player_uuid = (key)llList2String(api_event, 8);<br \/>\nstring game_event_type = llList2String(api_event, 9);<br \/>\ninteger game_event_severity = llList2Integer(api_event, 10);<br \/>\nstring game_event_subtype = llList2String(api_event, 11);<br \/>\n\/\/ Other variables can be set up based on the specific subtype.<br \/>\n\/\/ ==========================<\/p>\n<p>if (game_event_type == &#8220;GOOD&#8221;) {<br \/>\n\/\/ Something good happened to the player.<br \/>\n\/\/ game_event_severity goes from 1 (a tiny bit good) to 5 (life changing, miraculous, etc.)<\/p>\n<p>\/\/ Generic good event stuff.<br \/>\n} else if (game_event_type == &#8220;BAD&#8221;) {<br \/>\n\/\/ Something bad happened to the player.<br \/>\n\/\/ game_event_severity goes from 1 (a tiny bit bad) to 5 (catastrophic, suicide-inducing, etc&#8230;)<\/p>\n<p>\/\/ Subtype example for Greedy Greedy.<br \/>\nif (game_name == &#8220;Greedy Greedy&#8221; &amp;&amp; game_event_subtype == &#8220;BUSTED&#8221;) {<br \/>\n\/\/ It was a bad event, and specifically they busted (rolled nothing of value).<\/p>\n<p>\/\/ Sub type specific variables.<br \/>\ninteger points_lost = llList2Integer(api_event, 12);<br \/>\n\/\/ ^^^^ How many points they lost when they busted.<br \/>\n\/\/ ==========================<\/p>\n<p>\/\/ WOMP WOMP.<br \/>\n} else {<br \/>\n\/\/ Generic bad event stuff<br \/>\n}<br \/>\n} else if (game_event_type == &#8220;NEUTRAL&#8221;) {<br \/>\n\/\/ Something neutral happened to the player.<br \/>\n\/\/ game_event_severity doesn&#8217;t have a whole lot of meaning here, I guess. This is for events that aren&#8217;t good or bad, but still noteworthy.<\/p>\n<p>\/\/ Generic neutral event stuff.<br \/>\n}<\/p>\n<p>} else if (event_type == &#8220;WON&#8221;) {<br \/>\n\/\/ One or more players have won the game.<\/p>\n<p>\/\/ Set up event-specific variables.<br \/>\n\/\/ A lot of this information may be redundant if you&#8217;ve been keeping track of it up until now. This redundancy is carryover from the game uploading end-of-game data to the G.SL server.<br \/>\n\/\/ comma-sep-winners|winners_score|comma-sep-participants|comma-sep-participant-scores|comma-sep-player-counts(final,min,max)|comma-sep-options|duration<br \/>\nlist winners = llParseString2List(llList2String(api_event, 7), [&#8220;,&#8221;], []);<br \/>\n\/\/ The UUID(s) of the winner(s). There can be more than one winner in the case of ties or in the case of teams winning.<br \/>\ninteger winning_score = llList2Integer(api_event, 8);<br \/>\n\/\/ The score that the winner(s) had at the end of the game. Depending on the game, this might be the lowest score, highest score, or a total nonsense score.<br \/>\n\/\/ This is a single number, even if there are multiple winners, because all winners would have the same score, or else there wouldn&#8217;t be more than one of them.<br \/>\nlist all_players = llParseStringKeepNulls(llList2String(api_event, 9), [&#8220;,&#8221;], []);<br \/>\n\/\/ UUID list of all players who were playing the game. This includes anyone who LOST at some point during the game.<br \/>\nlist all_scores = llParseStringKeepNulls(llList2String(api_event, 10), [&#8220;,&#8221;], []);<br \/>\n\/\/ List of the final scores of all players playing the game. This is index-matched to the all_players list above.<br \/>\n\/\/ index-matched means llList2Integer(all_scores, N) will give you the correct score for llList2Key(all_scores, N) where N is the same for both.<br \/>\nlist player_counts = llParseStringKeepNulls(llList2String(api_event, 11), [&#8220;,&#8221;], []);<br \/>\ninteger final_players = llList2Integer(player_counts, 0);<br \/>\n\/\/ ^^^^ number of players still at the game when it ended.<br \/>\ninteger min_players = llList2Integer(player_counts, 1);<br \/>\n\/\/ ^^^^ Lowest concurrent number of players during the game&#8217;s entire duration.<br \/>\ninteger max_players = llList2Integer(player_counts, 2);<br \/>\n\/\/ ^^^^ Highest concurrent number of players during the game&#8217;s entire duration.<br \/>\nlist game_options = llParseStringKeepNulls(llList2String(api_event, 12), [&#8220;,&#8221;], []);<br \/>\n\/\/ ^^^^ This is a list of options that the game was played under. These are the same options on the Gaming.SL website for this game. Some games have no options, some have a couple, some have a lot.<br \/>\ninteger duration = llList2Integer(api_event, 13);<br \/>\n\/\/ Duration of the game in seconds.<br \/>\n\/\/ ==========================<\/p>\n<p>\/\/ Do winner stuff here. Money, hookers, booze! Or whatever, we&#8217;re not judging.<br \/>\n} else if (event_type == &#8220;LOST&#8221;) {<br \/>\n\/\/ A player has lost the game.<\/p>\n<p>\/\/ Not all games will send LOST messages.<br \/>\n\/\/ LOST messages are for elimination based games where a player can be removed from the game, but other players are still playing.<br \/>\n\/\/ Example of this would be Simopolis (Monopoly) where one player can go bankrupt, thus losing the game, but a final winner hasn&#8217;t been determined yet.<\/p>\n<p>\/\/ When a WON event is triggered, it is assumed that any remaining players have lost, but no specific LOST events will be called for each loser during a WON event.<\/p>\n<p>\/\/ Set up event-specific variables.<br \/>\ninteger player_index = llList2Integer(api_event, 7);<br \/>\n\/\/ ^^^^ player_index is the position they joined the game. If they sat in the first chair, they are &#8220;player 1&#8221;, if they sat in the second chair, they are &#8220;player 2&#8221;, etc.<br \/>\nkey player_uuid = (key)llList2String(api_event, 8);<br \/>\n\/\/ ==========================<\/p>\n<p>\/\/ A player lost. Whatever, I guess.<br \/>\n}<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n","protected":false},"author":1,"featured_media":0,"menu_order":10,"comment_status":"closed","ping_status":"closed","template":"","format":"standard","article-category":[71],"article-tag":[],"_links":{"self":[{"href":"https:\/\/karstenrutledge.com\/index.php\/wp-json\/wp\/v2\/article\/2857"}],"collection":[{"href":"https:\/\/karstenrutledge.com\/index.php\/wp-json\/wp\/v2\/article"}],"about":[{"href":"https:\/\/karstenrutledge.com\/index.php\/wp-json\/wp\/v2\/types\/article"}],"author":[{"embeddable":true,"href":"https:\/\/karstenrutledge.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/karstenrutledge.com\/index.php\/wp-json\/wp\/v2\/comments?post=2857"}],"version-history":[{"count":1,"href":"https:\/\/karstenrutledge.com\/index.php\/wp-json\/wp\/v2\/article\/2857\/revisions"}],"predecessor-version":[{"id":2858,"href":"https:\/\/karstenrutledge.com\/index.php\/wp-json\/wp\/v2\/article\/2857\/revisions\/2858"}],"wp:attachment":[{"href":"https:\/\/karstenrutledge.com\/index.php\/wp-json\/wp\/v2\/media?parent=2857"}],"wp:term":[{"taxonomy":"article-category","embeddable":true,"href":"https:\/\/karstenrutledge.com\/index.php\/wp-json\/wp\/v2\/article-category?post=2857"},{"taxonomy":"article-tag","embeddable":true,"href":"https:\/\/karstenrutledge.com\/index.php\/wp-json\/wp\/v2\/article-tag?post=2857"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}