{"id":39,"date":"2026-03-26T20:00:00","date_gmt":"2026-03-26T19:00:00","guid":{"rendered":"https:\/\/airex.se\/blogg\/racemotorn-till-servern\/"},"modified":"2026-03-26T20:00:00","modified_gmt":"2026-03-26T19:00:00","slug":"racemotorn-till-servern","status":"publish","type":"post","link":"https:\/\/airex.se\/blogg\/racemotorn-till-servern\/","title":{"rendered":"Vem r\u00e4knar ut vem som vann?"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">I ett spel d\u00e4r man t\u00e4vlar mot andra finns en fr\u00e5ga som avg\u00f6r allt annat: <em>vem r\u00e4knar ut vem som vann?<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Om svaret \u00e4r &#8221;spelarens webbl\u00e4sare&#8221; har man inte byggt ett spel. Man har byggt ett formul\u00e4r d\u00e4r deltagarna fyller i sina egna resultat.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Klienten \u00e4r en fiende som r\u00e5kar vara trevlig<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Det l\u00e5ter dramatiskt, men det \u00e4r den enda h\u00e5llbara utg\u00e5ngspunkten. Allt som k\u00f6rs i anv\u00e4ndarens webbl\u00e4sare kan l\u00e4sas, \u00e4ndras och ers\u00e4ttas. Det \u00e4r inte ett tecken p\u00e5 illvilja hos anv\u00e4ndaren \u2014 det \u00e4r en egenskap hos plattformen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I ett spel med ekonomi \u2014 d\u00e4r man k\u00f6per, s\u00e4ljer och t\u00e4vlar om pengar som betyder n\u00e5got f\u00f6r spelarna \u2014 blir det snabbt konkret. R\u00e4knas loppet i webbl\u00e4saren och resultatet skickas in, d\u00e5 \u00e4r resultatet en <em>\u00f6nskan<\/em>, inte ett utfall.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">S\u00e5 motorn flyttades till servern. Klienten skickar vad spelaren g\u00f6r; servern r\u00e4knar vad som h\u00e4nder och skickar tillbaka tillst\u00e5ndet \u00f6ver en \u00f6ppen f\u00f6rbindelse. Klienten ritar. Ingenting mer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Det g\u00f6r klienten dummare och hela systemet \u00e4rligare.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Buggarna som avsl\u00f6jade var sanningen bodde<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Under samma arbetspass fixade jag ett par fel som vid f\u00f6rsta anblick s\u00e5g orelaterade ut, men som visade sig ha samma rot.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Saldot uppdaterades inte efter en reparation.<\/strong> Pengarna drogs p\u00e5 servern, men vyn visade ett gammalt v\u00e4rde \u2014 f\u00f6r den l\u00e4ste fr\u00e5n n\u00e5got som bara fylldes vid inloggning. Det \u00e4r samma sorts fel som identitetsf\u00f6rv\u00e4xlingar och avdrift i konfiguration: <em>tv\u00e5 st\u00e4llen som p\u00e5st\u00e5r sig veta samma sak<\/em>, och bara det ena har r\u00e4tt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fixen var inte att uppdatera kopian oftare. Den var att sluta ha en kopia \u2014 vyn fr\u00e5gar servern, och fr\u00e5gan st\u00e4lls om efter varje h\u00e4ndelse som kan ha \u00e4ndrat svaret.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Och tv\u00e5 klassiska:<\/strong> klienten anropade en adress som inte fanns p\u00e5 servern, och l\u00e4ste ett f\u00e4lt som hette n\u00e5got annat i svaret. B\u00e5da \u00e4r triviala, b\u00e5da kostar en kv\u00e4ll, och b\u00e5da hade f\u00e5ngats av ett test som k\u00f6r p\u00e5 riktigt i st\u00e4llet f\u00f6r mot en p\u00e5hittad server.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Sjuttiofyra tester som klickar<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">D\u00e4rf\u00f6r blev n\u00e4sta steg en testsvit som startar en riktig webbl\u00e4sare, loggar in, klickar sig igenom spelet och kontrollerar vad som faktiskt st\u00e5r p\u00e5 sk\u00e4rmen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enhetstester hade inte hittat n\u00e5got av det ovan. Varje del fungerade utm\u00e4rkt f\u00f6r sig. Felen satt i <em>skarvarna<\/em> \u2014 mellan klient och server, mellan namn i ett svar och namn i koden som l\u00e4ser det. Skarvar syns bara n\u00e4r man g\u00e5r hela v\u00e4gen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">S\u00e5dana tester \u00e4r l\u00e5ngsammare och kr\u00e5ngligare \u00e4n enhetstester, och de betalar tillbaka f\u00f6rsta g\u00e5ngen n\u00e5gon \u00e4ndrar ett f\u00e4ltnamn p\u00e5 serversidan.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Vad jag tar med mig<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best\u00e4m var sanningen bor, en g\u00e5ng, och h\u00e5ll fast vid det.<\/strong> Det \u00e4r samma princip som g\u00e4ller f\u00f6r inventarium, f\u00f6r n\u00e4tverkskonfiguration och f\u00f6r identitet. Ett spelresultat \u00e4r inte annorlunda: n\u00e5gon r\u00e4knar, alla andra visar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ett cachat v\u00e4rde \u00e4r ett l\u00f6fte om att h\u00e5lla det uppdaterat.<\/strong> Det l\u00f6ftet bryts alltid, i n\u00e5got fl\u00f6de man inte t\u00e4nkt p\u00e5. Fr\u00e5ga i st\u00e4llet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Och en observation om att bygga spel med agenter: buggarna ovan \u00e4r exakt de en modell producerar n\u00e4r den skriver klient och server var f\u00f6r sig. Den h\u00e5ller inte kontraktet mellan dem i huvudet \u2014 den gissar rimliga namn. Testerna som k\u00f6r p\u00e5 riktigt \u00e4r inte en lyx i det arbetss\u00e4ttet. De \u00e4r det som g\u00f6r det m\u00f6jligt.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>Om svaret \u00e4r spelarens webbl\u00e4sare har man inte byggt ett spel, utan ett formul\u00e4r d\u00e4r deltagarna fyller i sina egna resultat. Om server-side-motorer och tester som klickar p\u00e5 riktigt.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-39","post","type-post","status-publish","format-standard","hentry","category-bygget"],"_links":{"self":[{"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/posts\/39","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/comments?post=39"}],"version-history":[{"count":0,"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/posts\/39\/revisions"}],"wp:attachment":[{"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/media?parent=39"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/categories?post=39"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/tags?post=39"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}