Projektrapport Spelet ”Graffiti Wars”

Download Report

Transcript Projektrapport Spelet ”Graffiti Wars”

Kungliga tekniska högskolan
Projektrapport
Spelet ”Graffiti Wars”
Grupp 6, DP09, VT2010
Projektrapport
TEMA:
Datortekniskt projekt 7,5hp
TITEL:
Graffiti Wars
GRUPP:
6
HEMSIDA:
http://www.oskarlind.com/gw
DELTAGARE:
Mikael Berg
Fan Ding
Oskar Lind
Robin Lindahl
HANDLEDARE:
Anders Lindström, [email protected]
DATUM:
19 maj 2010
EXAMINATOR:
Anders Lindström, [email protected]
Sammanfattning
I kursen HI1010 Datortekniskt projekt våren 2010 fick en grupp bestående av Mikael Berg, Fan Ding,
Oskar Lind och Robin Lindahl i uppdrag av kunden Johnny Panrike att utvecka en produkt. Gruppen
fick ett antal riktlinjer och krav för godkänt betyg att utgå ifrån, bland annat att produkten skulle vara
en nätverksapplikation enligt klient-server-modell, att ett applikationsnivåprotokoll för
nätverkskommunikationen skulle upprättas, samt att koden skulle vara skriven i programspråket C och
vara välskriven och väldokumenterad.
Gruppen genomförde en faktainsamling utifrån kraven på projektet. Därefter upprättades en
kravspecifikation och problemformulering i samband med kunden Johnny Panrike och
projekthandledaren Anders Lindström. Ett kommunikationsprotokoll och en genomarbetad
moduldesign utformades, som sedan implementerades med hjälp av de metoder som gruppen valt för
projektet. Med hjälp av bland annat versionshantering, ett programmeringsbibliotek för
plattformsoberoende och användande av parallell programmering, etablerades en god grund för
projektets genomförande som även kunde uppfylla de tekniska kraven.
Resultatet blev ett spel som fick namnet ”Graffiti Wars”. Det utspelar sig i 2D-miljö, och går ut på att
”tagga”, d.v.s. måla sin egen symbol, på olika mål med andra medspelare samtidigt i bild. Varje spelare
har en egen symbol och kan klottra över andras målningar. Under en tidsomgång på ca. 2 minuter ska
spelaren försöka måla sin symbol på så hög andel av målen som möjligt. När spelomgången är slut
jämförs spelarnas andelar och den som fått högst andel vinner.
Spelet drivs med hjälp av en server som kan hantera upp till 4 st. klienter samtidigt. Klienten kan köras
i både Linux- och Windowsmiljö.
Nyckelord: SDL, SVN, Kodorganisering, Moduler, Header-filer, IDE, Versionshantering, Nätverk, C,
Programmering.
Abstract
In the course HI1010 Computer Engineering Project (Datortekniskt projekt) in spring 2010, a group
consisting of Mikael Berg, Fan Ding, Oskar Lind and Robin Lindahl was assigned the task of
developing a software product, by the customer Johnny Panrike. The group was given a number of
guidelines and course goals, for example that the product had to have networking capabilities according
to a client-server-model, that an application level protocol had to be established, and that the code was
to be written in the programming language C.
The group gathered facts around the subjects that had to be researched for the project. After that, a
specification was produced in cooperation with the client, Johnny Panrike, and the course supervisor,
Anders Lindström. A communications protocol and a very thorough module design document was
produced, which then was implemented according to the methods which the group had chosen for the
project. With the help of, for example, version control, a common platform-independent programming
library and the use of parallel programming, a solid foundation for the project had been set that would
also meet the technical demands for the software.
The result was a game called “Graffiti Wars”. Set in a 2D-environment, the objective is to spray your
“tag” over as many targets as possible, competing against up to four other players, showed on the
screen in real time. Each player has its own “tag” and can paint over other players' tags, if the target has
already been painted on. During the game session time of 2 minutes, the objective is to have your tag
sprayed over as many targets as possible by the end of the game session. The player that has the highest
share of targets when time is up wins.
The game is run by a server that can handle up to 4 clients at the same time. The client can be run both
in a Linux and Windows environment.
Keywords: SDL, SVN, Modules, Header-files, IDE, Version Control, Network, C, Programming.
Förord
Arbetet i projektet har fungerat bra i samtliga faser. Efter en relativt lång analysfas, då gruppen även
utformade en detaljerat moduldesign, kunde problemen lösas all eftersom projektet fortskred.
För att kunna tillgodogöra sig rapporten på ett tillfredsställande sätt bör läsaren ha vissa förkunskaper i
programmering och nätverksteknik. De kodexempel som visas är, om inget annat anges, skrivna i C.
Projektet är utfört i kursen Datatekniskt projekt i årskurs 1 på programmet Datorteknik med inriktning
Program- och systemutveckling, KTH Teknik och Hälsa.
Vi vill tacka Anders Lindström som varit vår handledare.
Projektgrupp 6
KTH Teknik och Hälsa, Campus Haninge
2010/05/19
_________________________________
_________________________________
Mikael Berg
Fan Ding
_________________________________
_________________________________
Oskar Lind
Robin Lindahl
Innehåll
1 Inledning.................................................................................................................................................1
1.1 Problemdefinition.......................................................................................................................1
1.2 Mål..............................................................................................................................................1
1.3 Lösningsmetoder.........................................................................................................................2
1.4 Avgränsning................................................................................................................................2
2 Om spelet...............................................................................................................................................3
3 Teoretisk referensram..............................................................................................................................5
3.1 Arbetsmetod................................................................................................................................5
3.2 Programspråk..............................................................................................................................6
3.3 Versionshantering.......................................................................................................................6
3.4 Kodorganisering.........................................................................................................................7
3.5 Utvecklingsverktyg (IDE)..........................................................................................................8
3.6 Plattformsoberoende...................................................................................................................9
3.7 Grafik........................................................................................................................................10
3.8 Parallell programmering..........................................................................................................10
3.9 Nätverksprogrammering..........................................................................................................10
3.10 Händelsehantering.................................................................................................................11
3.11 Testning..................................................................................................................................11
4 Arbetsmetoder och kodorganisering.....................................................................................................13
4.1 Plattformsskillnader..................................................................................................................13
4.2 Versionshantering.....................................................................................................................14
4.3 Utvecklingsverktyg...................................................................................................................14
4.4 Kodorganisering.......................................................................................................................15
4.5 Pekare.......................................................................................................................................16
4.6 Structar......................................................................................................................................16
4.7 Testning.....................................................................................................................................16
5 Nätverkskommunikation.......................................................................................................................17
5.1 Implementering i klient och server...........................................................................................17
5.2 Nätverksbelastning...................................................................................................................18
5.3 Informationsutbyte....................................................................................................................18
6 Server....................................................................................................................................................21
6.1 Parallell programmering...........................................................................................................21
6.2 Serverns uppgifter.....................................................................................................................21
6.3 Felhantering och säkerhet.........................................................................................................22
7 Klient.....................................................................................................................................................23
7.1 Moduler på klientsidan.............................................................................................................23
7.2 Grafik........................................................................................................................................24
7.2.1 Transparens...................................................................................................................25
7.2.2 Animationer..................................................................................................................25
7.3 Filhantering...............................................................................................................................27
7.4 Händelsehantering....................................................................................................................27
7.5 Spellogik...................................................................................................................................29
8 Slutsats..................................................................................................................................................31
8.1 Projektarbetets fortskridande....................................................................................................31
8.2 Framtida arbete / Saker som inte hanns med............................................................................32
9 Ordförklaringar.....................................................................................................................................35
10 Referenser...........................................................................................................................................37
Appendix A: Moduldesign.......................................................................................................................39
Appendix B: Kravspecifikation...............................................................................................................43
Appendix C: Kommunikationsprotokoll..................................................................................................45
Appendix D: Användarmanual.................................................................................................................49
Appendix E: Testning...............................................................................................................................53
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
1 Inledning
I kursen Datatekniskt projekt våren 2010 ska grupp 6 utvecka en produkt utformad för en specifik
kund, i detta fall Johnny Panrike. Gruppen kommer att utveckla ett spel som går under arbetsnamnet
”Graffiti Wars”. Spelet går ut på att i ett sidoscrollande plattformsspel i 2D ”klottra” på anslagstavlor
med andra medspelare samtidigt i bild.
1.1 Problemdefinition
En server behöver kunna hantera flera klienter samtidigt. Till detta kommer vi behöva använda oss av
parallell programmering, och för kommunikationen även sockets.
För att kunna åstadkomma ett gränssnitt med rörlig grafik behöver vi också sätta oss in i ett sådant
bibliotek, samt bli bekanta med hur man åstadkommer bland annat en sidoscrollande värld och
kollisionshantering mellan objekt.
Det är tänkt att alla objekt på banorna ska kunna initieras på olika positioner beroende på vad som läses
in från en textfil. För att få till detta behöver gruppen tillämpa filhantering.
Spelkontrollen kommer först och främst att involvera tangentbordstryckningar, men på sikt även
musrörelser. Händelsehantering behövs i koden, för att applikationen ska utföra något när användaren
trycker på en knapp.
Vi behöver på något sätt också dela upp vår kod i överskådliga funktioner i sammanlänkade moduler,
ha rikligt med kommentarer och se till att alla kan arbeta med samma kod utan att förstöra för varandra
eller skriva över en lösning som kanske fungerade tidigare.
1.2 Mål
Punkterna i kravspecifikationen kan anses uppnådda när vi uppnått följande mål:

En server, körandes på en Linuxdator, ska genom parallell programmering kunna ta emot
anslutningar från fyra klienter och starta upp en spelomgång. Försöker ytterligare en klient
ansluta ska denna kopplas ned. När en spelande klient avslutar ska dess resurser på servern
frigöras.

Klienter ska gå att starta på både Windows- och Linuxdatorer, och ska kunna hantera och rita
upp upp till fyra spelare samtidigt.

Det grafiska gränssnittet ska bestå av en bakgrund, varpå rörliga spelare i form av rektanglar
kan förflytta sig. Andra rektanglar, spelets måltavlor, ska kunna byta färg efter att spelare varit
framme och klottrat på dem.

Klottring ska ske, som minst, genom att spelare åker fram till en måltavla och trycker på en
tangent. För högre betyg krävs att klottring sker genom att utföra rörelser med musen.

Förflyttning i x-led ska ske med hjälp av piltangenterna. För högre betyg ska även hopp,
förflyttning i y-led, kunna ske genom tangenttryck.

Grafiken kan utvecklas till att tillåta sidoscrollning. När en spelare rör sig ut till kanten av
1(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
skärmen så ska vyn ändras, så att varje spelare får en individuell vy av ett större landskap.

Spelegenskaper ska kunna förändras genom att spelare plockar upp ett föremål, en så kallad
powerup. Till exempel ska spelare på så vis kunna få ökad åkhastighet.

Objektens positioner på banorna ska kunna regleras genom inläsning från separata textfiler, som
användare lätt kan gå in och ändra.

Både server- och klientapplikationen ska ha inbyggd felhantering, så att klienter kopplas ned
och avslutas utan fel efter avslutad spelomgång, och att resurser frigörs på servern om en klient
kraschar.
1.3 Lösningsmetoder
Gruppen behöver ha ett system för att inte skriva över varandras kod. Versionshantering är ett sätt att
uppnå detta, och det finns lite olika tekniker att välja mellan. SVN är en sådan metod, men det finns
även alternativ som CVS, BZR och Git.
Det behövs ett sätt för att kunna dela upp kod i flera moduler. Header-filer är en metod för att uppnå
just detta. Filerna kommer behöva länkas samman för att känna till varandra. Detta går att åstadkomma
med manuella make-kommandon, eller automatiserat i en utvecklingsmiljö.
Parallell programmering behövs för att servern ska kunna hantera flera klienter samtidigt. Här kan man
använda antingen trådar eller barnprocesser. Windows och Linux åstadkommer dessa saker på olika
sätt, och det kan vara värt att använda ett plattformsoberoendebibliotek som till exempel SDL.
Ett grafikbibliotek behövs för att rita upp ett gränssnitt. Ett välkänt sådant är OpenGL, ett annat finns
inbyggt i SDL.
Ett bibliotek som kan hantera tangentbordstryckningar och musrörelser behövs. Även sådana
funktioner finns i SDL.
Data kommer att skickas över nätverket i någon form av paket och genom sockets. Alternativ som finns
här är UDP- och TCP-paket, samt sockets genom Socket.h, IO::Socket eller SDL_net.
För att beslut ska kunna fattas kring vilka tekniker som lämpar sig bäst för vårt ändamål kommer
faktainsamling behöva göras inom respektive område. Sökning kommer framförallt att ske på Internet,
men kan även innefatta litteratur från KTH:s bibliotek.
1.4 Avgränsning

Gruppen ska inte använda sig av 3D-grafik, utan endast 2D med hjälp av OpenGL

Servern ska inte byggas för att kunna hantera fler klienter än fyra samtidigt

Det ska endast gå att måla spelarens specifika bild, inte olika bilder för varje tillfälle

Data ska inte överföras på XML-format utan istället som textsträngar

Klienten behöver inte ha stöd för andra plattformar än Linux och Windows

Det kommer inte att finnas chattfunktion under själva spelomgången
2(57)
Graffiti Wars
2
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Om spelet
Illustration 1: In-game sekvens Graffiti Wars
Spelet går ut på att rivaliserande spelare strider om att ta över ett så stort revir som möjligt med sin
grafitti. Karaktärerna är representanter från olika klaner inom den undre världen, som kämpar för att
sprida sitt rykte genom att klottra sina respektive ”tags” på alla platser de kan tänka sig. Det här gör de
för att uppnå status och därmed respekt gentemot varandra. Då klottrandet är en ständig kamp mot
klockan och den jagande poliskåren, tar sig karaktärerna i huvudsak fram snabbt med hjälp av inlines.
En spelomgång börjar med att upp till fyra spelare äntrar banan. En nedräkning från 100 sekunder
startas, och under den tiden har spelarna som uppgift att klottra på så många objekt som möjligt (se
Illustration 1: In-game sekvens Graffiti Wars). Det är också möjligt att klottra där någon annan tidigare
klottrat, och därmed ”måla över” den spelarens konst. När nedräkningen nått sitt slut äntrar polisen
scenen, varpå spelet tar slut och karaktärerna flyr fältet. Den spelare som klottrat på flest objekt går
vinnande ur striden.
Karaktärerna förflyttar sig i sidled med hjälp av piltangenterna eller A- och D-tangenterna och har
också möjlighet att hoppa med space-tangent. När spelaren kommer fram till ett objekt som han eller
hon vill klottra på trycks vänster musknapp ned, varpå en instruktion kommer att dyka upp på skärmen
om i vilken riktning spelaren ska röra musen (åt vänster eller åt höger), hela tiden med musknappen
nedtryckt.
Att klottra en ”tag” kostar tre sprayburkar. Spelaren har från början tillgång till tio sprayburkar, och kan
fylla på sitt förråd genom att åka och hämta påfyllning.
Spelaren kommer ha möjlighet att åka och hämta så kallade ”power-ups”, som kan vara utplacerade på
3(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
mer eller mindre svårtillgängliga platser, t.ex. genom att hoppa upp längs flera plattformar. Exempel på
”power-ups” kan vara att spelaren under en tid får ökad åkhastighet eller påfyllning av sprayburkar.
4(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
3 Teoretisk referensram
Här presenteras de teoretiska ramar som användes för projektet. Inledningsvis genomfördes en
faktainsamling som gav stöd åt besluten om vilka metoder som skulle användas. En del av ramarna för
projektet var givna i förväg, såsom att applikationen skulle vara nätverksbaserad, innehålla ett grafiskt
gränssnitt och andra krav för betygsnivån som gruppen avsedde att nå. Andra begränsningar och
metoder valdes av gruppmedlemmarna själva för att kunna åstadkomma extra funktionalitet eller
förenklingar i arbetet.
3.1 Arbetsmetod
För att kunna effektivisera samarbete i grupp skall en arbetsmetod väljas innan projektet sätts igång.
Det som gäller i programutvecklingen hade vi inte så många alternativ att välja bland.
Vattenfallsmodellen och iterativ utveckling är de två främsta arbetsmetoder att välja bland. Med
vattenfallsmodellen arbetar man fas efter fas, i efterhand kommer man inte kunna gå tillbaka till en fas
som redan är avslutad. Med iterativ utveckling menas att en fas ”repeteras” flera gånger tills man blir
nöjd. Denna arbetssätt är mycket väl anpassad i utveckling av programvara. Testkörning sker efter varje
ändring av koden för att se till att den verkligen fungerar som tänkt. Vid missnöje kommer koden att
ändras och testköras igen, tills man blir nöjd.
Den agila utvecklingsmetoden är väl anpassad för mjukvarautveckling. Med denna metod blir
utvecklingsprocessen lättrörlig. Scrum och Xtreme Programming är två välkända agila
utvecklingsmetoder. För att förkorta utvecklingstiden fokuserar man på kommunikationen mellan
utvecklare och beställare, och lägger upp mindre uppdateringar ofta.
Istället för att följa den klassiska vattenfallsmodellen där man betar av fas för fas, skulle gruppen
använda sig av en mer iterativ arbetsprocess, enligt Xtreme Programming-modellen. Det här innebar
bland annat att vi skulle köra testning av programmets moduler löpande, allt eftersom de skrevs. Vidare
skulle även faktainsamling att ske vid behov under hela projekttiden. Demonstration av produkten
skedde för beställare även innan den var färdigställd, för att stämma av om arbetet fortskridit i rätt
riktning.
Tanken var att bygga upp programmet av ett antal mindre enheter, moduler, för vilka minst en
gruppmedlem skulle ansvara men där det var önskvärt att minst ytterligare en medlem ger kommentarer
eller testkör modulen.
Det kom så småningom att bli så att gruppmedlemmarna sällan ensamma burit ansvar för enstaka
moduler, utan inledningsvis delades de två största modulerna upp på två personer var, och sedan
gjordes enskilda insatser för att implementera funktioner i de båda. Även i funktionsimplementeringen
har det i stor utsträckning handlat om parprogrammering; med en person som skrivit koden och en
person som varit med och hjälpt till att lösa problemet. I många fall har det här säkert inte varit det
mest effektiva sättet att arbeta, men det har resulterat i att det ofta funnits åtminstone två personer som
varit insatta i hur en funktion eller modul fungerar.
Beträffande löpande testning, så har sådan inte skett för varje enskild funktion i särskilt stor skala, då
man, kanske lite väl naivt, konstaterat att funktionen inte ska kunna ge upphov till så många fel så
länge den anropas med rätt data. Det byggdes sällan hela testmoduler för att testa just färdigskrivna
5(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
moduler; utom i fallet då vi skrev kommunikationsdelen hos servern och samtidigt skrev en testklient
som skickade hårdkodade strängar.
Arbetet har ändock skett i en iterativ process där modulerna byggts på med små delar i taget, och där
man provkört för att se om allt fungerat som avsett innan ytterligare funktionalitet byggts på. Det har då
ofta handlat om att koppla upp sig mot servern med flera klienter samtidigt, för att säkerställa att allt
fortfarande fungerar.
3.2 Programspråk
Det finns många olika programspråk, bland annat C, C++ och Java. Ett av dessa språk kommer att
användas för programmeringen av spelet.
När vi programmerar i C behöver vi inte undra om det finns något operativsystem som inte kan köra
på. Den andra fördelen med C är att C är lätt tillämplig, d.v.s. språket abstraktionsnivå är lagom och lätt
att acceptera.
Abstraktion inom programmeringen innebär att det finns bara en del information att presentera och
resten döljer sig. Det enklaste exempel att bevisa detta är siffror och funktioner som används både i C
och matematik. T.ex. siffra 3 kan beskriva en mängd av en vara, men endast siffran 3 har inga
motsvarande föremål i verkligheten. Det är samma princip när det gäller funktioner.
I Stardard C ingår inga grafikritande bibliotek som vi kan användas oss utav, utan vi var tvungna att
söka andra tekniker att förverkliga vårt spel med. Tack vare C:s popularitet och bidrag från andra
programmare, har vi möjlighet att välja biblioteke såsom SDL, OpenGL m.fl1. Mer om dessa bibliotek
beskrivs i nedanstående avsnitt.
Den största skillnaden mellan C och Objektorienterad programspråk (förkortas som OO-programspråk i
nedanstående text) är att det går inte att implementera klasser och arv i C. Vid utveckling av stora
programvara, har det stor betydelse att programmera objektorienterad. M.h.a arv i OO-programspråk
ger möjligheter att återanvända ytterligare mer kod än vad modulerna kan göra, och ger ännu bättre
programstruktur. Om vi jämföra tidsfördelningen, att programera med OO-programspråk innebär att
programmaren ägna sig mer tid åt att designa programmet och lägga till mindre tid i kodning och
implementation. Sammanfattningsvis kan vi konstatera att OO-progarmspråk som Java är bättre
anpassad till större projekt, särskilt för utveckling av nätverkbaserad dataspel2.
Projektet genomfördes med hjälp av programspråket C, uppdelat i moduler med hjälp av header-filer
och tillhörande .c-filer. Mer om implementationen nämns i kap 4.4 , 4.5 , 4.6 samt kap. 7 och
Appendix 1 och 3.
3.3 Versionshantering
Vid utveckling av programvara i form av grupp, är det ibland nödvändigt att flera gruppmedlemmar
ändrar i samma fil samtidigt. Det blir annars svårt att organisera koden om bidragen från alla är lika
viktiga.
Ett versionshanteringssystem löser problemen som uppstår när flera arbetar med samma fil/filer.
1
2
Wikipedia: http://en.wikipedia.org/wiki/C_standard_library
Wikipedia: http://sv.wikipedia.org/wiki/Objektorienterad_programmering
6(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Gruppmedlemmar måste först ta reda på om det finns nya uppdateringar av koden innan man göra
ändringar i den.
Ett problem som med versionshanteringssystem generellt är att den föregående versionen av filen, vid
omedveten uppläggning, skrivs över. Det finns huvudsakligen två lösningsmetoder till detta problem:

Lock - Modify – Unlock
Personen som laddar ner den senaste versionen av filen kan låsa samtidigt, för att börja göra
ändringar. Andra gruppmedlemmar är då tvungna att vänta tills låsningen tas bort. Risken med
denna lösning är att den personen som låste filen glömmer att ta bort blockeringen, vilket
medför fördröjning av grupparbetet.

Copy - Modify – Merge
Ett annat lösningsalternativ är att om två gruppmedlemmar laddar ner samma version av koden
och gör ändringar, och sedan lägger upp varsin ”senaste version”, kommer
versionshanteringssystemet att fråga den användare som lagt upp sist, om koden från olika
bidrag ska läggs ihop eller vilken del som skall behållas.
Det är inte bara samarbetet i gruppen som underlättas av versionshantering, utan även möjligheterna att
gå tillbaka till tidigare versioner av kod. Det finns mer än tio versionshanteringssystem att välja bland3.
Kravet för att kunna använda versionshantering är att man har tillgång till en server som ska kunna
spara alla filändringarna och att alla gruppmedlemmar ska kunna använda det valda
versionshanteringssystemet på sin programmeringsplattform.
3.4 Kodorganisering
Till skillnaden från programmen som vi skrev i kursen Programmering Grundkurs, bedömer gruppen
att kodmängden till detta projekt kommer att bli relativt stor. Den enklaste lösningen är att skriva all
kod i samma fil, men på grund av kodmängden och med hänsyn till den valda iterativa arbetsmetoden
kommer detta ej att vara praktiskt möjligt.
En lösning för behovet av kodorganisering i C, är att bygga program i moduler med hjälp av headerfiler. Detta underlättar för programmaren när det gäller test och felsökning av enskilda moduler. Under
utvecklingsprocessen sker testning i första hand separat, d.v.s. oberoende av andra moduler. Med
välstrukturerade moduler ges dessutom en bra lättläslighet för alla. Det underlättar även i utvecklingsoch underhållsarbetet.
I språket C utgörs header-filer av en fil med ändelsen .h. Till detta hör en .c-fil som innehåller själva
implementationen av funktionerna som deklarerats i .h-filen. Ett exempel på en .h-fil (foo.h) är
#ifndef _FOO_H
#define _FOO_H
#define FOO_BAR 42;
int foo_bar(int x);
#endif
och en tillhörande c-fil (foo.c):
#include "foo.h"
int foo_bar(int x)
{
3
Versionhantering (Wikipedia): http://sv.wikipedia.org/wiki/Versionshantering
7(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
return x*FOO_BAR;
}
4
. Med hjälp av denna struktur kan programmet enkelt byggas upp av moduler med deklarationimplementation.
3.5 Utvecklingsverktyg (IDE)
En IDE (Integrated Development Environment) effektiviserar kodarbetet genom att tillhandahålla en
enda gemensam miljö för att författa, modifiera, kompilera, implementera och felsöka kod.
För att effektivisera kodningsarbete, är vanliga texteditorer inte längre ett alternativ. Åtminstone ett
utvecklingsverktyg måste programmeraren vara bekant med. Det tar säkert tid, men det lönar sig
långsiktigt. Syftet med att koda i utvecklingsverktyg är att programmeraren inte behöver ha koll på små
grejer utan fokuserar på själva programdesignen och på att tänka logiskt.
En av de mest grundläggande funktionerna är att programmet automatiskt ger förslag till
structmedlemmar. Efter att programmeraren angett structens namn och punktnotation, listar
utvecklingsverktyget ut samtliga medlemmar av structen. Programmeraren behöver inte bläddra i
koden och får en bättre överblick. Utvecklingsverktyget markerar även icke-använda variabler och
felaktiga notationer, och utför kodindentering automatisk.
För gruppen är det självklart nödvändigt att välja ett IDE som kan hantera språket C. Då detta språk är
väldigt vanligt i programmeringsvärlden finns det här många alternativ. Några är emellertid större och
mer populära än andra, och gruppen har valt att fokusera på de som presenteras i
föreläsningsanteckningen ”Utvecklingmiljöer.pdf” av handledaren Anders Lindström. Dessa är:

Netbeans (Open Source, GNU/LGPL, http://netbeans.org)

Eclipse (Eclipse Public License, http://www.eclipse.org/)

Code::Blocks (GPL, http://www.codeblocks.org/)

Visual Studio (Microsoft, http://www.microsoft.com/visualstudio/sv-se/products/2010-editions)
NetBeans IDE och Eclipse skiljer inte så mycket från varandra. Personer som är bekanta med den ena
kan arbeta med den andra utan problem. Både Codeblocks och Visual Studio ger möjlighet att dölja kod
(implementation) i funktionen. Det går då snabbare att bläddra till det kodstycke som man vill ändra i.
Ett annat sätt för snabbåtkomst är att man välja funktioner direkt i ”funktionslistan”.
Med komplicerade algoritmer i ett stort program kan det vara tidsödande att hitta och rätta felen. Med
hjälp av en inbyggd debugger kan programmet köras igenom rad för rad för att hitta felet.
Med Visual Studio och andra IDE:s visas eventuella programmeringsfel direkt för programmeraren
medan han/hon skriver koden. Det hjälper programmeraren att rätta felet under arbetets gång utan att få
så mycket felmeddelanden efter kompileringen. Visual Studio har dessutom en inbyggd kompilator
som utvecklades av Microsoft självständigt. Alla presenterade utvecklingsverktyg ger möjlighet att
länka till bibliotek såsom SDL och OpenGL. Efter korrekt länkning med olika bibliotek går det att
inkludera dessa i projektets källkodsfiler.
Windows har ingen inbyggd kompilator och denna måste således installeras separat (detta gäller ej vid
4
Organisera kod (pdf) (Anders Lindström)
8(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
användning av Visual Studio, som har inbyggd kompilator). För detta finns två gratisalternativ:

MinGW (http://www.mingw.org)

CygWin (http://www.cygwin.com)
Linux levereras däremot med inbyggd kompilator och debugger. Däremot går det inte att köra Visual
Studio naturligt i Linux. Avändare av detta operativsystem kan därför endast använda (utifrån ovan
nämnda alternativ) Eclipse, Netbeans eller Codeblocks. I Linux används GCC som kompilator5.
Att programmera med olika IDE inom gruppen borde inte innebära några problem. Under
faktainsamlingsfasen har emellertid vissa skillnader i hanteringen av biblioteken upptäckts, som t.ex.
att sökvägen till biblioteken vid include-satserna är olika i Windows och Linux. För inkludering av t.ex.
SDL-biblioteken skrivs i Linux
#include <SDL/foo.h>
och i Windows
#include <foo.h>
.
3.6 Plattformsoberoende
Plattformsoberoende kommer, om möjligt, att användas så att spelet kan spelas med personer som har
ett annat operativsystem.
När det gäller plattformsoberoende så innebär det för detta projekt förenklat att spelet ska kunna köras
på fler än ett operativsystem, såsom Windows, Linux eller Mac. Genom att välja ett
”multiplattformsbibliotek” (eng. ”cross-platform”) vid programmeringen kan man få ett fungerade spel
som kan spelas på olika datorer, utan att behöva kompilera om programmet för varje operativsystem.
På engelska kallas detta ”cross-platform”.
SDL (Simple Direct Media Layer) är ett multiplattformsbibliotek skrivet i C, som tillhandahåller ett
enkelt interface för interaktion mellan grafik, ljud, nätverk och andra i/o-enheter i en dator. SDL
använder inbyggda funktioner för att interagera med t.ex. Direct X för grafik i Windows, Xlib i Linux
och Quartz i MacOS X6. Genom att använda SDL:s inbyggda funktioner kan alltså programmeraren
enkelt använda sig av grafik, nätverk och annat i sin kod utan att behöva bekymra sig om
kompatibilitetsproblem mellan plattformarna.
De stora funktioner som kommer att behövas för projektet, som befaras vålla kompatibilitetsproblem
om de inte hanteras med ett multiplattformsbibliotek, bedöms av gruppen vara grafiken och
nätverksfunktionaliteten. Vid efterforskningar framgår att SDL innehåller funkioner för detta genom
biblioteken SDL_net och SDL_image (se kap. 2.7 Grafik).
SDL är uppdelat i olika system: Video (hanterar bilder och OpenGL), Audio (ljud), CD-ROM, Joystick
och Timer. Dessa initieras automatiskt genom kommandot
SDL_Init(SDL_INIT_EVERYTHING);
5
6
Utvecklingsmiljöer (pdf) (Anders Lindström)
Simple Directmedia Layer (Wikipedia): http://en.wikipedia.org/wiki/Simple_DirectMedia_Layer
9(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
. Vid sidan av dessa system finns några standardbibliotek som ger extra funktionalitet. Dessa är
SDL_image
SDL_mixer
SDL_net
SDL_ttf
SDL_rtf
Då dessa ingår som standard i SDL finns god dokumentation kring funktionerna och många exempel på
hur implementationen sker i C7.
3.7 Grafik
Grafik kommer att användas till spelet för underlätta för spelaren, så spelaren slipper se en massa text i
en konsol.
Spelet kommer att behöva ett grafiskt användargränssnitt (GUI), för att kunna visa var i spelet spelaren
är, var måltavlorna är placerade och dessutom veta var motståndarna är. Det finns en del grafiska
bibliotek, och två av dessa är SDL och OpenGL. SDL har dessutom stöd för att använda OpenGL
grafik i SDL fönster, med hjälp utav ett bibliotek som heter SDL_OpenGL.
3.8 Parallell programmering
Parallell programmering används för att program ska effektivare kunna utföra funktioner. Detta
kommer att användas så att spelet kan utföra många fler saker samtidigt, som att rita ut spelare,
förflytta spelare och liknande, utan att behöva göra så att spelet går långsammare.
För att med ett och samma program kunna hantera flera sessioner samtidigt, som körs oberoende av
varandra, krävs någon form av parallell programmering. Det går att låta huvudprocessen skapa
barnprocesser för att hantera dessa parallella skeenden, eller använda sig av så kallade
lättviktsprocesser; trådar. De senare delar på ett och samma minnesutrymme, och är därmed en mer
effektiv lösning. Det här kommer framförallt vara nödvändigt för att låta flera klienter vara anslutna
samtidigt till en och samma serverapplikation; vilket är ett krav för att flera datorer skall kunna spela
med varandra.
3.9 Nätverksprogrammering
Spelet ska ha möjligheten att spelas över ett nätverk. För detta krävs att gruppen gör ett val om vilket
protokoll som ska användas, vilka bibliotek som ska användas för uppgiften samt hur det ska
implementeras.
I valet mellan nätverksprotokoll finns det två stora att välja emellan: TCP (Transmission Control
Protocol) och UDP (User Datagram Protocol). För projektet är det viktigt att undersöka vilket av
alternativen som passar bäst för varje enskild uppgift i nätverksöverföringen, och sedan ta ett beslut om
vilken teknik som ska användas. Dock behöver inte samma protokoll användas för alla olika uppgifter,
utan vissa paket kan skickas med en typ av protokoll och andra med något annat.
Skillnaden mellan TCP och UDP är främst säkerheten och den hastighet med vilken paketen kan
7
Simple Directmedia Layer (Wikipedia): http://en.wikipedia.org/wiki/Simple_DirectMedia_Layer
10(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
överföras. TCP har större ”header” än UDP, som innehåller fält för att kunna kontrollera och verifiera
paketen. Detta gör att en mindre mängd data får plats i själva paketet än när de skickas med UDP. En
större mängd total data måste därför överföras med TCP för att skicka samma information. Därför är
TCP ”långsammare” än UDP i praktiken8.
UDP innehåller få eller inga kontrollfunktioner och paketen som skickas riskerar därför att ”tappas
bort” på vägen. Det kan därför vara lämpligt att använda detta protokoll för överföring av data som
skickas i stora mängder, men inte är så känslig för om det tappas bort paket på vägen.
När det gäller funktionalitet för nätverk inom C-programmering är det för gruppen aktuellt att
undersöka vad som erbjuds för bibliotek som är plattformsoberoende. Eftersom gruppen valt att
använda sig av SDL för många av de andra funktionerna inom projektet, är det lämpligt att främst hålla
sig till SDL:s funktioner för nätverk.
SDL:s nätverksfunktionalitet tillhandahålls genom biblioteket SDL_net9. Om detta inte redan finns
installerat på den plattform som kommer att användas för kodarbetet, måste detta installeras separat.
När SDL_net sedan ska användas i koden måste biblioteket sedvanligt inkluderas och sedan initieras:
#include "SDL_net.h"
SDLNet_Init();
SDL_net används sedan på följande sätt på klientsidan för att skapa sockets och skicka data:
SDLNet_ResolveHost(); // Tar in IP och portnr för att skapa nödvändig koppling till
servern
SDLNet_TCP_Open(); // Öppna en anslutning
SDLNet_TCP_Send(); // Skicka TCP-paket över socket
På serversidan krävs något ytterligare kod. Sammantaget lämpar sig SDL_net väl för det som gruppen
vill åstadkomma när det gäller nätverksfunktionalitet.
3.10 Händelsehantering
För projektet krävdes hantering av händelser såsom knapptryckningar, musrörelser och lyssnande efter
”flaggor” som satts beroende på om något skulle inträffa eller inte.
Eftersom gruppen valde att använda SDL för de flesta funktioner, fokuserades på SDL:s eget bibliotek
för att hitta lösningar för händelsehanteringen. SDL har gott stöd för detta, bl.a. genom fullt
internationellt keyboard support, inbyggda funktioner för att lyssna till tangenttryckningar, och flera
funktioner för att tolka musrörelser och positioner. SDL:s eventhantering initieras enkelt med
SDL_Init().
3.11 Testning
För att säkerställa att programvaran fungerar som den ska, och för att eliminera så många felaktigheter i
koden som möjligt, krävdes tester av projektet.
Alla delar av projektet kan testas med hjälp av ”White-box testing” och ”Black-box testing”. I Whitebox testing fokuseras på funktionella krav som t.ex. huruvida det går att manipulera servern genom att
8
9
Cisco Internetworking Technology Handbook:
http://www.cisco.com/en/US/docs/internetworking/technology/handbook/Internet-Protocols.html
SDL_net 1.2: http://www.libsdl.org/projects/SDL_net/
11(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
skicka felaktiga data (”fault injection”), eller om man på något annat sätt kan ändra i en fungerande
klient eller server så att den beter sig felaktigt (”mutation testing”).
Black-box testing genomförs genom att testa icke-funktionella krav. Programmeraren kan t.ex. välja att
skicka slumpdata över en socket till servern (”fuzz-testing”), testa gränser för olika aspekter i
programmet (”boundary value analysis”) eller att hitta på tester baserat på hur programmet har satts
ihop och vilka moduler som är lämpliga att testa (model based).
Dessa tester kan utföras t.ex. med fokus på kommunikationen mellan projektets olika kodmoduler,
kommunikationen mellan server-klient eller interaktionen människa-dator.
12(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
4 Arbetsmetoder och kodorganisering
För att projektet skulle kunna genomföras smidigt och effektivt rekommenderades gruppens
medlemmar att använda sig av t.ex. versionshantering, utvecklingsverktyg och arbetssättet Extreme
Programming. Vissa av metoderna presenterades initialt genom lärarledda föreläsningar, men en del av
dem krävde mycket faktainsamling på egen hand. Givet var att applikationen skulle skrivas i C. Detta
medförde att kodorganisering, utvecklingsverktyg och adresseringsmetoder med hjälp av till exempel
pekare automatiskt kom i fokus när faktainsamlingen kring arbetsmetoder och kodorganisering skulle
börja. Structar och pekare uppfattades som en naturlig ingrediens i språket C och information var lätt
att hitta. Metoder för testning introducerades av Johnny Panrike på en föreläsning 2010-03-29, och
versionshantering av Mårten Palm 2010-04-12.
4.1 Plattformsskillnader
Grupp #6 valde att använda SDL för att åstadkomma bland annat händelsehantering, trådar och
nätverksfunktionalitet som fungerar i både Windows- och Linuxmiljö. Detta för att slippa behöva koda
specifika moduler för respektive plattform, då vissa andra etablerade systemanrop som C använder sig
av skiljer sig mellan de två operativsystemen.
Trots att gruppen arbetat i olika operativsystem har så gott som all kod vi skrivit fungerat på både
Windows och Linux. Det här har vi programspråket C och bra kompilatorer att tacka för, men också
biblioteket SDL som helt befriat oss från att sätta oss in i skillnader mellan systemen. Den
huvudsakliga anledningen till att vi tänkte använda SDL från början var för att hitta en
plattformsoberoende metod för att åstadkomma parallell programmering med trådar. Det har sedan
visat sig att vi bara kom att använda trådar på serversidan, på vilken vi endast hade plattformskravet
Linux. Då serverapplikationen skrevs med hjälp av SDL så fick vi dock av bara farten en applikation
som även gick att köra felfritt under Windows, vilket visat sig vara väldigt praktiskt vid individuell
testning av klienten man arbetar på.
När vi använde SDL stötte vi på omdirigering av STDOUT och STDERR, så att alla våra printf-satser
skrevs till textfiler istället för till terminalen. Då detta inte var önskvärt i vår applikation, bland annat
för felsökningens skull, var vi tvungna att dirigera tillbaka utskriften till skärmen igen. Det här visade
sig vara krångligt att få till, speciellt på ett sätt som fungerade i både Windows och Linux. Till slut
hittade vi satsen freopen(”CON”, "w", stdout). I Linux fungerade det dock inte att skriva ”CON” som
sträng, utan där skulle istället stå ”NULL”. Problemet löste vi genom att i början av koden, eller
närmare bestämt i våra headerfiler, skriva:
#ifdef __linux__
#define CON NULL
#else //#ifdef _WIN32
#define CON "CON"
#endif
Något som vi också vara tvungna att ta ställning till var hur vi skulle skicka data mellan servern och
klienten. Hade vi valt att skicka binär data hade detta fungerat annorlunda för Windows och Linux, så
vi valde istället att skicka all data som strängar. Så länge strängarna initierats på rätt sätt på båda sidor,
och rätt längd skickats och tagits emot, så har det fungerat felfritt.
13(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
En omfattande skillnad som drabbade gruppens Linuxanvändare och till allas vår förvirring orsakade
ett Socket-felmeddelande varje gång klienten startades, berodde på att vi råkat namnge en av våra
funktioner till samma sak som ett systemkommando: connect().
I Linuxmiljö drabbades vi också då och då av diverse Segmentation Faults. Dessa visade sig bero på en
så enkel sak som att filnamnen ibland innehöll versaler, medan kod inte gjorde det, eller vice versa.
Vi fick också vid ett tillfälle uppleva hur Linux dödade processen för att den började ta upp för mycket
minne. Allt vi fick som felmeddelande var att processen efter en viss tid försökte köra pthread_cancel(),
utan att lyckas. Till slut lyckades vi identifiera att det handlade om en minnesläcka, som vi snabbt
täppte till.
När vi skulle använda funktionen getch() i biblioteket <conio.h> kunde vi enkelt inkludera detta
bibliotek i Windows, där det fanns inbyggt, men var i Linux tvungna att först installera det manuellt.
4.2 Versionshantering
För att kunna återgå till äldre upplagor av kodade moduler och för att lätt kunna hantera när flera
medlemmar gör ändringar i samma fil bestämde vi oss tidigt för att använda ett
versionshanteringssystem. Verktyget vi valde heter Subversion vilket stödjer både Windows och Linux.
Grupp #6 har tillgång till en SVN-server där all kod kommer att sparas. Gruppmedlemmarna kan
därmed inte lägga upp sina filändringar utan att först ta ställning till uppdateringar som andra har gjort.
Gruppen använde sig av versionshantering i stor utsträckning, framförallt för att snabbt och lätt kunna
hämta hem de senaste versionerna av samtliga moduler och headerfiler, men också i många fall för att
sätta ihop förändringar som flera användare gjort i samma fil. I vår grupp använde vi Subversion, SVN,
genom verktygen Tortoise SVN (i Windows) och Rabbit VCS (i Linux). Vi var hela tiden flitiga med att
ladda upp ändringar så att övriga i gruppen kunde ta del av dem, vilket förstås ofta också ledde till så
kallade konflikter. Trots en del lite nervösa försök att pussla ihop ändringar som skett på flera ställen i
en fil så fick vi faktiskt aldrig problem med att någon fil inte fungerade efteråt. Annars fanns alltid
tryggheten i att kunna gå tillbaka till det omtalade stadiet: ”men det fungerade ju igår”.
SVN visade sig vara en räddare i nöden då vi dagen innan inlämning plötsligt sprang på helt
oförklarliga problem med vårt program. Då vi inte alls kunde identifera vilken av dagens ändringar som
åstadkommit dessa fel – vilka framförallt bestod i att spelet grafik hackade och att till exempel
musrörelser inte registrerades – valde vi vid dagens slut att gå tillbaka till den senast fungerande
versionen från dagen före. Det här var mycket smidigt att åstadkomma genom SVN, som utan tvekan
ett smidigt verktyg som vi hade haft väldigt svårt att klara oss utan.
4.3 Utvecklingsverktyg
För att framförallt underlätta kodarbetet, men också för att lätt hantera länkning mellan header- och
källkodsfiler, uppmuntrades samtliga gruppmedlemmar använda sig av en valfri utvecklingsmiljö.
I gruppen har många integrerade utvecklingsmiljöer använts, som alla vållade gruppmedlemmarna
problem som antingen ledde till otaliga konfigurationer av fillänkningen eller att utvecklingsmiljön helt
sonika övergavs till förmån för en annan.
I gruppen har vi haft en gruppmedlem som arbetat i Ubuntu Linux. Övriga har kört Windows i
14(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
variationerna XP, Vista och 7. I Linux har Eclipse, efter att ha ersatt NetBeans, lyckats stå kvar som en
relativt stadig, men inte helt problemfri, utvecklingsmiljö in i det sista. Windows 7-datorn körde
frivilligt Microsofts utvecklingsmiljö från början, medan de två andra tvingades söka sig till sin
bundsförvant Visual Studio den hårda vägen.
Av någon anledning som vi inte lyckades identifiera så ville applikationen inte rita ut mer än en spelare
på skärmen när vi körde fungerande Visual Studio-kod i Code::Blocks och Eclipse med MinGW. Inga
felmeddelanden under kompilering eller körning, men ändå oönskat resultat. Det är dock inte möjligt
för oss att vara helt säkra på att felet ligger i varken utvecklingsmiljön eller kompilatorn, utan det kan
lika gärna ha varit en konfigurationsmiss eller att koden vi skrivit kunde ha varit bättre.
Under större delen av projektet har det dock fungerat bra att alla gruppmedlemmar använt olika
konfigurationer, vilket resulterat i kod som går att bygga och köra (nästan) oberoende av
utvecklingsmiljö och operativsystem. Samma kod gick i varje fall att köra både i konfigurationen
Windows + Visual Studio och Linux + Eclipse.
En sak som skiljde mellan Visual Studio och övriga utvecklingsmiljöer var att sökvägen till vissa
bibliotek såg annorlunda ut. I Visual Studio var sökvägen till SDL.h helt enkelt <SDL.h>, medan den i
övriga utvecklingsmiljöer var <SDL/SDL.h>. Det här problemet löste vi genom följande kodsnutt i
början av våra filer:
#ifdef _MSC_VER
#include <SDL.h>
#else
#include <SDL/SDL.h>
#endif
4.4 Kodorganisering
För att beskriva vad en implementerad modul i slutändan ska innehålla kommer så kallade headerfiler(.h) att användas. En header fungerar som en ”ritning” och beskriver vilka variabler och funktioner
en modul måste innehålla, tillsammans med omfattande kommentarer. Alla ovannämnda
utvecklingsverktyg stödjer att skapa projekt. Moduler och headerfiler läggs in i samma projekt. Till
varje källkodsfil (.c) filen finns det alltid en motsvarande headerfil (.h). Headerfilen innehåller inte mer
än konstanter, variabler, deklaration av struct och funktionsprototyper. Vid läsning av headerfilen kunna
läsaren få reda på vad motsvarande källkodsfilen innehåller.
Gruppen satte i stort sett inte igång med det grova kodarbetet förrän en i alla fall preliminär
moduldesign fanns. Den här bestod framförallt i att dela upp programmet i en server- och en
klientapplikation, och att sedan ha flera mindre moduler för att bygga upp respektive applikation.
Ett problem som gruppen dragits med är att de två största modulerna på både klient- och serversidan
tenderade att växa till sig närmast okontrollerat. Det här har inte inneburit problem för själva
applikationen, men har gjort att det tagit onödigt lång tid att hitta rätt i koden. Dessutom är det en dålig
idé att uteslutande använda sig av globala variabler, vilket vi i stort sett gjorde överallt inledningsvis.
Ett grepp togs för att omorganisera koden till mindre funktioner med lokala variabler som skickas dem
emellan, ofta med hjälp av pekare.
15(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
4.5 Pekare
Trots att vi hade en omfattande modul- och funktionsdesign så räckte den inte riktigt till. Att inte
specificera parametrar eller argument för de olika funktionerna, samt vilka lokala variabler de skulle
ha, ledde till slarv i form av en stor mängd globala variabler (vilket kanske inte alltid är så bra när man
använder sig av flera moduler, där flera moduler kan ha variabler med samma namn). Med globala
variabler gör man det lätt för sig, då funktioner inte behöver ta några argument alls utan direkt kan gå
in och göra ändringar i alla variabler de vill. Ett alternativ är att ha vanliga funktioner, av till exempel
typen int, som vid anrop returnerar ett heltal efter att ha gjort någon form av beräkning. Det här
fungerar emellertid inte när en funktion behöver ändra i flera variabler samtidigt, eller i en array, eller
flera arrayer. Istället får funktionen som parameter då ta emot en pekare till önskad variabels adress, till
vilken den sedan skriver ny data.
4.6 Structar
För att på ett praktiskt sätt kunna gruppera variabler som på något sätt hörde ihop använde vi oss av så
kallade structar, något vi också kom i kontakt med genom vår första programmeringskurs i C. Ett
logiskt sätt för att tänka ut när en struct kunde tyckas behövlig var att tänka i vilka objekt vi hade i
spelet. Allra enklast var det att se att vi behövde en struct som innehöll egenskaperna som
representerade en spelare; med bland annat dess position i x- och y-led samt dess bredd och höjd. Fler
naturliga objekt som behövde structar var våra måltavlor, våra powerups och våra plattformar; helt
enkelt allting som skulle ritas ut på skärmen.
På serversidan använde vi en struct för att representera varje klient, där det ingick bland annat en
TCPsocket för varje klient.
Structar kan också vara användbara för att enkelt skicka över flera variabler som argument till en
funktion. På klientsidan användes detta till viss del för att hålla reda på alla variabler som
representerade ”en spelomgång”, och som därmed alltid behövde skickas till spelets uppdateringscykel.
4.7 Testning
Gruppen valde att utföra Black-box- och White-box-tester enbart på kommunikationen server-klient.
Gruppen använde sig av Fuzz-testing, Boundary value analysis, Model based testing, Fault injection
och Mutation testing. Testerna utfördes i tre faser:
1. Test av klienten, kommunicerande med en testserver
2. Test av servern, kommunicerande med en testklient
3. Test av kommunikationen mellan servern och klienten
Testerna dokumenterades under arbetets gång, med fokus på projektets slutfas då koden skulle till
största delen vara färdigställd. TCPsocket för varje klient.
16(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
5 Nätverkskommunikation
I ett spel där det anses viktigt att ta emot skickad data i rätt följd och att data inte försvinner längs
vägen blir TCP det naturliga valet, eftersom UDP inte kontrollerar vilka skickade paket som kommer
fram eller inte, och därmed aldrig heller skickar om förlorade paket. Det här är inget som vi vill belasta
vår applikation med, och förlitar oss därmed hellre på transportlagret och TCP.
Innan vi satte igång och programmerade kommunikationsdelen skrevs ett kommunikationsprotokoll (se
Appendix C: Kommunikationsprotokoll), där det i detalj framgick i vilken ordning data skulle skickas
mellan server och klient. Tanken var från början att den viktigaste informationen skulle skickas via
TCP, såsom information om vilken färg måltavlorna har och om en powerup försvunnit från spelplanen
eller inte. Sådan information skulle också bara skickas vid behov; först efter att en flagga som skickas
över TCP sätts till något annat än 0 skulle servern sedan göra sig redo för att ta emot den förväntade
informationen. Oftast skulle då bara ett par TCP-flaggor, dels för att tala om om det fanns information
att skicka, dels en exit-flagga som klienten kunde sätta vid avslut, skickas. Den här planen följdes
också under implementeringen.
Mindre viktig information, som varje spelares individuella positionsförändringar, skulle kunna skickas
med UDP, eftersom det inte skulle vara viktigt att varje liten förändring kom fram. UDP-paket är, som
vi lärde oss i kursen Datakommunikation, betydligt kortare bytemässigt än TCP-paket, som har mycket
så kallad ”overhead”, bitar som används för att hålla reda på bland annat sekvens- och
kontrollinformation. Kommunikation över TCP blir därmed inte lika effektiv, eftersom det på varje
mängd data också går en mängd kontrollinformation.
Vid användning av UDP skulle ett sekvensnummer behöva läggas till i början av datan för att ens
motspelare inte skulle få gamla positioner, men i övrigt skulle viss ”ryckighet” vara okej ifall vissa
positioner saknades under spelarens framfart. Den här tanken implementerades dock ej, utan
spelarpositioner fick istället precis som den andra informationen också att skickas över TCP, i varje
uppdateringscykel. Anledningen till det här var att vi valde att prioritera annan funktionalitet i spelet
och att det inte riktigt kändes som det var nödvändigt med tanke på den ändå inte särskilt stora
genomströmningen av trafik.
5.1 Implementering i klient och server
För kommunikationen använde vi sockets från SDL_net, vilka var väldigt enkla att sätta sig in i, kanske
speciellt efter att ha provat på sockets i språket Perl i kursen Operativsystem. Servern är byggd för att
kunna ta emot nya anslutningar när en av dess fyra sockets är lediga, och att därefter bara skapa en
tillfällig socket för att tala om för anslutande klienter att det är fullt, för att sedan genast koppla ner
dessa. Länge var det möjligt för nya spelare att hoppa in mitt i spelet efter att gamla hoppat ut, men vi
valde senare att för spelets skull sätta begränsningen att inga nya spelare ska kunna hoppa in mitt i en
spelomgång. Efter att en spelomgång tagit slut, när timern på servern nått noll, så kommer servern
starta om sig själv och koppla ner alla anslutna klienter (som förhoppningsvis redan hanterat spelets
slut på ett lämpligt sätt) och döda alla trådar.
Ytterligare felhantering på servern implementerades i projektets slutfas, bland annat för att hantera
klienter som kraschat. Servern startas om efter en spelomgångs slut. Om en klient anslutit och sedan
kraschat innan en spelomgång börjat, kan servern hantera detta. Om en klient kraschar mitt i en
17(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
spelomgång kommer servern inte startas om förrän serverns spelomgångsklocka nått noll. Ytterligare
felhantering skulle vara möjlig, bland annat skulle servern kanske behöva validera klienterna på något
sätt, för att sätta stopp för att obehöriga klienter kopplar upp sig.
5.2 Nätverksbelastning
Serverns huvudsakliga uppgift är att skicka information mellan klienterna. Inga beräkningar sker här,
utan endast den information som anses vara nödvändig för att varje klient ska kunna rita ut alla spelare
och föremål på rätt plats och med rätt egenskaper utbyts.
Illustration 2: Nätverksbelastning, först då servern kör utan någon ansluten klient. Efter första
höjningen har en klient anslutit och befinner sig i lobbyn. Vid andra ökningen så har spelaren
startat spelet.
Det hade varit möjligt att istället bygga spelet genom ett tyngre belastat nätverk, och mindre belastade,
så kallade ”tunna klienter”. Det här är relativt vanligt idag då nätverkskapaciteten ofta är god och då
många användare har små lättviktiga datorer utan så mycket prestanda. Vi har valt att göra det
omvända, och låta klienterna använda sin egen dators hårdvara för att hantera spelets logik och grafik. I
efterhand kan det här valet definitivt diskuteras och omvärderas; då vi upplevt en del problem när vi
kört applikationen på svagare maskiner. Det kan också tyckas som att man inte riktigt går hela vägen i
att satsa på låg nätverksbelastning när man skickar all data med TCP-paket, utan UDP hade varit att
föredra för en del information.
5.3 Informationsutbyte
För kommunikation över TCP är det viktigt att den data som skickas och tas emot mellan servern och
klienten gör så i exakt rätt ordning; så att det finns en motsvarande Receive-funktion på andra sidan,
när en Send-funktion anropas på den ena. Det är förstås också viktigt att rätt längd anges på strängarna,
så att varken mer eller mindre information skickas; då den annars kan komma att ”rinna över” till andra
strängar och orsaka oväntade resultat. I våra applikationer följer vi nedanstående ordning:
18(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Nr.
Klienten
Servern
1
Ta emot välkomstmeddeande/id-nummer
Skicka välkomstmeddelande/id-nummer
2
Skicka hälsning
Ta emot hälsning
3
Ta emot game timer
Skicka game timer
4
Ta emot spelarstatus (ansluten/ej ansluten)
Ta emot spelarstatus (ansluten/ej ansluten)
5
Skicka ytterligare data (om målade targets
eller upplockade powerups)
Ta emot ytterligare data (om målade
targets eller upplockade powerups)
6
Ta emot targetfärger
Skicka targetfärger
7
Ta emot powerupstatus (tagen/ej tagen)
Skicka powerupstatus (tagen/ej tagen)
8
Skicka positioner för den egna spelaren
Ta emot positioner för den egna spelaren
9
Ta emot positioner för samtliga spelare
Skicka positioner för samtliga spelare
10
Skicka exitflagga (1 = avsluta, 0 = fortsätt)
Ta emot exitflagga (1 = avsluta, 0 =
fortsätt)
Tabell 1: Informationsutbyte server – klient.
Efter att ny data tagits emot hos klienten kommer dess lokala arrayer och variabler att få nya värden,
som sedan används till att rita upp aktuell grafik. Hos servern kommer globala arrayer att uppdateras,
så att samtliga trådar kommer åt den nya informationen, för att kunna skicka den vidare till sina
respektive klienter.
Illustration 3: Ett urval av den data som servern växlat med klient 0. Utskriften kommer från
serverns terminal. Observera att alla strängar som skickas inte står med här.
19(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
20(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
6 Server
En Linux- och Windowskompatibel server konstruerades med hjälp av Parallell programmering för att
kunna hantera flera klienter samtidigt genom s.k. separata trådar. Server-programvaran bestod av
modulen ConnectionHandler, där funktionerna
int handleClient(void *d);
void restartServer();
int main(int argc, char **argv);
fanns implementerade. Funktionen handleClient kördes av varje tråd för att hantera varje klient.
Funktionen restartServer höll ordning på spelomgångarna och stängde ute eller släppte in klienter
beroende på om en spelomgång kördes eller inte. Funktionen main lyssnade efter nya anslutningar och
innehöll implementationer byggda på SDL:s nätverksbibliotek SDL_net.
6.1 Parallell programmering
För att åstadkomma parallell programmering och tillåta flera funktioner att köras parallellt, och inte i en
rak följd efter varandra med stora väntetider, valde vi att använda oss av trådar på serversidan. Där
skulle varje tråd representera varsin ansluten klient, som den ständigt utbytte data med genom sockets.
Trådarna har dels sina egna lokala variabler, men också några gemensamma arrayer som varje tråd
sedan skickar i sin helhet till sina respektive klienter.
Varje tråd skriver bara i sitt eget område i arrayen, och därmed var det aldrig nödvändigt att använda
någon form av låsmekanism eller mutex.
Trådar i SDL initierades med hjälp av funktionen SDL_CreateThread i biblioteket SDL_thread. För att
hålla reda på när server var full implementerades funktionen med hjälp av en if-satsen
if (clients[i].socket = SDLNet_TCP_Accept(server)){
clients[i].thread = SDL_CreateThread(handleClient,(void *) clients[i].id);
clients[i].busy = 1;
connections++;
}
där connections räknades upp för att matchas mot max-spelarantalet (i vårt fall 4 st).
När klienten sedan skulle kopplas fri och avslutas användes SDL_KillThread:
SDL_KillThread(clients[i].thread);
6.2 Serverns uppgifter
Servern använder som bekant trådar för att hantera klienterna; en tråd för varje klient. En array av
typen struct Client används för att hålla reda på alla klienter servern behandlar. Main-funktionen går
ständigt igenom denna array för att se om det finns lediga platser kvar, och tillåter isåfall nya
inkommande anslutningar så länge spelet inte har startats.
När en ny anslutning upprättats kommer main-funktionen genast att initiera en post i klientarrayen;
med ett lämpligt id-nummer, en egen TCP-socket och en busy-flagga som sätts till 1 medan platsen i
arrayen fortfarande är tagen. Därpå skapas en tråd, som kommer att köra funktionen handleClient för
att påbörja informationsutbytet. Funktionen kommer att få klientens id-nummer som parameter, och
21(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
kan sedan använda denna för att läsa och skriva till rätt positioner i den globala klientarrayen.
Efter att main-tråden initierat eventuella nya anslutningar, kommer den gå över till sin andra uppgift,
nämligen att rensa upp bland de trådar som inte längre används. När en klient kört färdigt kommer den
att sätta en flagga i klientarrayen, exitthread, till ’1’, vilket betyder att maintråden har fritt fram att ta
bort klienten ur arrayen, stänga dess socket och döda dess tråd. Main-tråden fortsätter sedan att utföra
dessa två uppgifter: att lyssna efter nya anslutningar samt att rensa upp och göra plats för nya.
Om en klient skulle försöka ansluta medan servern är full kommer en tillfällig socket att skapas. Genast
skickas ett meddelande till klienten om att servern är full, och välkommen att försöka igen senare, och
sedan kopplas klienten och socketen ned. Samma socket kan även användas för att tala om att en
spelomgång redan körts igång; vilket i praktiken betyder samma sak för klienten: var god försök igen
senare.
Servern kan bara hantera en spelomgång i taget. På servern finns förutom alla strängar som skickas
fram och tillbaka mellan dess klienter även en timer, en klocka som håller reda på hur mycket som är
kvar av en spelomgång. Normalt sätts denna till 100 sekunder. Main-tråden räknar ner denna timer,
som sedan skickas vidare ut till varje klient genom deras respektive trådar och sockets.
6.3 Felhantering och säkerhet
Enligt ovan har serverns main-tråd hand om att göra plats för nya klienter när tidigare anslutna kopplar
ner. När klienten på sin sida av nätverket väljer att avsluta applikationen, kommer så inte att göras
förrän en flagga skickats till servern om att klienten vill avsluta; vilket därmed även kommer få tråden
på servern att hoppa ur sin loop och markera att den vill dödas av main-tråden genom att klientens
exitthread-flagga sätts. På så vis kommer inga döda trådar att fortsätta köras på servern efter att
klienten avslutat.
Även efter att en spelomgångs timer nått noll kommer servern att rensa upp bland klienterna, genom
anrop till funktionen restartServer(). Denna funktion anropas också så fort det inte längre finns några
klienter anslutna till servern, för att snabbt göra plats för en helt ny spelomgång med helt nya klienter.
Att klienten alltid avslutar på ”rätt sätt” är förstås önskvärt, men i verkligheten kan man inte kallt räkna
med att det alltid går som det ska. Därför behövs någon form av felhantering. Om klienten av någon
anledning skickar paketen i fel ordning eller skickar paket av fel längd kommer servern att reagera
genom att snabbt be den tråd som representerar klienten att hoppa ur sin loop och markera sig själv för
att dödas. Den här felhanteringen är lätt att åstadkomma; då SDLNet_TCP_Recv och
SDLNet_TCP_Send båda returnerar -1 om något går fel.
22(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
7 Klient
Klientdelen byggdes upp av en mängd header- och källkodsfiler. För att åstadkomma de funktioner som
projektet krävde, användes metoder för plattformsoberoende, grafik, nätverk, filhantering och
händelsehantering. Vissa av de metoder som först planerades ändrades eller byttes ut under arbetets
gång, vilket kan sägas ha berott på arbetsmetodens iterativa process, där ändringarna utvärderades och
testades alleftersom. T.ex. byttes metoden för grafik ut från ren OpenGL till SDL:s inbyggda funktioner
för bildbehandling, eftersom de ansågs kunna tillgodose behoven för projektet trots SDL:s relativt
begränsade möjligheter.
7.1 Moduler på klientsidan
Följande moduler används på klientsidan:
Implementation
Header-fil
Client.c
Client.h
Communicate.c
Communicate.h
Game.c
Game.h
GameOver.c
GameOver.h
Graphics_SDL.c
Graphics.h
Lobby.c
Lobby.h
Obstacle.c
Obstacle.h
Player.c
Player.h
Powerup.c
Powerup.h
Target.c
Target.h
Tabell 2: Moduler på klientsidan
På klientsidan finns main-modulen Client.c, som först och främst använder sig av den stora modulen
Game.c. Det här är en stor modul som innehåller en timer och en funktion som konstant lyssnar efter så
kallade Events. Den viktigaste händelsen är den som kallas på av timern, nämligen spelets
uppdateringscykel. Här sker kommunikationen med servern, samt all logik för att svara på den nya data
som tagits emot. Resultatet ritas sedan ut på skärmen genom att kalla på funktioner i modulen
Graphics. På klientsidan finns också de mindre modulerna Player, Target, Obstacle, Powerup och Map,
som innehåller structar för att representera respektive objekt.
Game.c använder funktioner i modulen Communicate.c för att skicka data till och ta emot data från
servern. Här sker också själva anslutningen, nedkopplingen och allt som har med sockets och SDL_net
att göra. På så vis skulle man i ett senare skede på ett transparent sätt kunna byta ut Communicatemodulen mot en annan, som till exempel använde sig av UDP istället för TCP. För att rita ut grafik
kallar Game.c på modulen Graphics_SDL.c genom Graphics.h. Även här hade det varit möjligt att i ett
senare skede byta ut grafikhanteringen, mot till exempel OpenGL. Förutom Game.c som sköter själva
23(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
spelsessionen så finns även Lobby.c som har hand om uppstarten och uppkopplingen mot servern
(genom Communicate.c) samt GameOver.c som har hand om att presentera resultatet. Var och en av
dessa tre moduler har varsin egen händelsehanterare som svarar olika på knapptryckningar.
7.2 Grafik
För att åstadkomma ett grafiskt gränssnitt underlättar det att använda sig av ett bibliotek med kraftfulla
funktioner för just detta ändamål. Detta verkar finnas inbakat direkt i SDL, men fler möjligheter öppnar
sig med biblioteket OpenGL. Som av en händelse verkar de två biblioteken lämpa sig väldigt bra ihop;
där en rityta lätt skapas med SDL-anrop är det sedan möjligt att rita med hjälp av OpenGL-diton.
Till slut kom vi att implementera vår grafikmodul med hjälp av grafikfunktionerna i SDL.
Inledningsvis hade vi varit inställda på att använda OpenGL, men då vi inte lyckades få koll på
bilduppdateringen ordentligt svängde vi snabbt över för att se om det skulle vara enklare att använda
SDL. Det visade sig vara det, och för spelets skull innebär det ingen brist då vi inte planerat varken 3Dgrafik eller några roterande objekt. Att använda SDL för grafiken visade sig vara väldigt smidigt och
enkelt, och vi hade kanske lärt oss mer av att göra allting den hårda vägen via OpenGL; men då det
handlar om att prioritera för att möta alla krav valde vi att nöja oss med SDL, i alla fall inledningsvis.
Då grafiken helt och hållet sköts av en egen grafikmodul är den förstås helt utbytbar – och
inledningsvis hade vi två moduler som hette Graphics_SDL respektive Graphics_GL, där den senare
övergavs men lätt skulle kunna återinföras i ett senare skede.
Och ännu enklare att få till okej grafik var det om man använde sig av bilder, vilket vi gjort uteslutande.
Vi installerade visserligen SDL_image, men så länge man håller sig till .bmp-bilder så räcker de
inbyggda funktionerna i SDL, även för att få till bilder med en transparent alfakanal.
Grafik i SDL kunde ritas upp med hjälp av funktionerna
SDL_BlitSurface(srcimg, &src, canvas, &dst);
SDL_FreeSurface(srcimg);
där srcimg sedan tidigare har laddats med exempelvis
SDL_Surface *srcimg = SDL_LoadBMP("trashcan.bmp");
vilket representerar bildfilen med spelaranimation 4. Applikationen följer alltså mönstret rita-”blitta”freesurface. SDL_BlitSurface lägger alltså ut det som ritats på skärmen, medan SDL_FreeSurface
frigör ritytan.
Modulen Graphics_SDL.c med tillhörande Graphics.h-fil användes för att rita upp all grafik i
applikationen. I Graphics_SDL.c implementerades följande ritfunktioner:
Funktionsnamn
Syfte
initGraphics();
Initiera Truetype-fonts, skärmupplösning m.m.
refreshGraphics();
Uppdatera grafiken mha. SDL_Flip().
drawText();
Rita upp text. Tar emot text via dess inparametrar.
drawBackground();
Rita upp bakgrundsfilen bg.bmp.
drawTrash();
Rita upp soptunnor i spelplanen (endast
24(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
dekorativt).
drawPlayer();
Rita upp spelarna, x- och y-positioner samt
riktning som inparametrar.
drawTarget();
Rita upp måltavlorna, x- och y-positioner som
inparametrar.
drawPowerup();
Rita upp powerups, x- och y-positioner som
inparametrar.
drawObstacle();
Rita upp plattformarna som spelare kan hoppa upp
på, x- och y-positioner som inparametrar.
Tabell 3: Funktioner i Graphics_SDL.c.
Ovanstående ritfunktioner (Tabell 3: Funktioner i Graphics_SDL.c.) ritades alltså upp på ritytan och
”blittades”, alltså visades på skärmen, med hjälpa av SDL_BlitSurface. Därefter frigjordes ritytan med
SDL_FreeSurface.
7.2.1
Transparens
En transparent alfakanal kunde åstadkommas med hjälp av SDL-funktionen SDL_SetColorKey.
Funktionen tar en given färg i RGB-format och tilldelar den som en nyckel för SDL-grafiken att skala
bort. För denna applikation valdes att göra bilderna med en helt vit bakgrund, så att RGB-värdet
(255,255,255) kunde tillhandahållas som ”alfakanal”:
SDL_SetColorKey(srcimg, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(srcimg->format,
255, 255, 255));
där srcimg är den källfil som läses och som alltså blir av med färgkanalen (255,255,255).
7.2.2
Animationer
Animationer användes för spelarfigurerna i applikationen. Tre olika typer av animationer
implementerades: Spelarrörelser, sprayanimationer och stillastående animation. Metoden
implementerades i Graphics_SDL.c, och använde sig av en stor bildfil (.bmp) per spelare som lästes av
från olika koordinater beroende på vilken rörelse som ville visas.
25(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Illustration 4: Spelaranimation
Illustration 4: Spelaranimation visar animationsdelarna för spelarfärg röd. Som synes i bilden
infogades alla typer av animation för spelaren i samma bild. För att åstadkomma animationen lästes
filen av i olika steg. Om t.ex. en animation av en högergående rörelse skulle ritas upp valdes att läsa av
filen längst upp från vänster till höger och sedan om igen. Vice versa om en vänstergående rörelse
skulle åstadkommas. Sprayanimationen placerades på tredje raden, och en ”stillastående” animation
längst ner.
Kodexemplet
srcimg = SDL_LoadBMP("PlayerAnim4.bmp");
src.x = x;
if (direction == 1){
src.y = 0;
}
else if (direction == 2){
src.y = 120;
}
else if (direction == 0){
src.y = 360;
}
26(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
else if (direction == 3){
src.y = 240;
}
visar hur avläsningen sker genom att modifiera en ”offset”, src y så att bilden avläses från ett annat yvärde beroende på vilken animation man vill åt (direction == 1, direction == 2, etc.).
7.3 Filhantering
För att läsa in positioner till våra måltavlor, plattformar och powerups använde vi oss av textfiler, vilka
vi förstås kom åt genom funktioner för filhantering. Det här är ett område alla i gruppen bekantat sig
med i den inledande C-kursen. De filer som behövde läsas i spelet var filerna Obstacles.txt, Targets.txt
och Powerups.txt, som innehöll koordinater och, i särskilda fall, andra egenskaper för plattformarna,
måltavlorna och ”powerups”. Koordinaterna skrevs på formatet
xxxx yyyy z
eftersom x- och y-koordinaterna kunde innehålla maximalt 4 siffror. Dessutom lämnades en plats för ett
eventuellt z-fält som kunde innehålla en övrig egenskap för objekten. T.ex. bestod Powerups.txt av
0250
0790
1430
2070
2350
0150
0150
0150
0150
0350
0
1
0
1
0
där 0 eller 1 representerade om objektet skulle ge flera sprayburkar eller ge ökad rörelsehastighet för
spelaren.
För att läsa filerna användes C:s inbyggda filhanteringsfunktioner fopen och fscanf. Värdena lagrades
sedan i en struct enligt mönstret
void initTargets(struct Target t[]) {
FILE *targetsfile;
targetsfile=fopen("Targets.txt","rt");
fscanf(targetsfile,"%d",&value);
t.x = value;
fclose(targetsfile);
}
där funktionen initTargets kallades på hos klienten inför varje ny spelomgång.
Det är helt öppet för klienter att gå in och ändra i textfilerna. Dock är kontrollerna av den data som
skrivits in sparsmakad; och vi får därför utgå ifrån att användaren sköter sig, följer det givna formatet
och skriver koordinater som befinner sig någonstans på spelets yta, i både x- och y-led. För att
fortfarande kunna spela helt rättvisa spelomgångar mot sina motspelare krävs det också att de får ett
exempler av den ändrade textfilen; då koordinaterna för objekten inte skickas vidare till servern. Det
hade varit önskvärt att låta spelare dela banor med varandra direkt genom spelet, men den
implementeringen har helt enkelt inte hunnits med.
7.4 Händelsehantering
För att hantera tangentbordstryckningar och musrörelser använde gruppen SDL Events. Spelets
huvudloop lyssnar ständigt efter nya events, och svarar sedan på dessa genom att sätta flaggor till 0
27(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
eller 1. Den viktigaste händelsen som utlyses är spelets uppdateringscykel, som aktiveras av spelets
SDL Timer 20 gånger per sekund. Här hanteras alla de flaggor som satts av knapptryckningarna, och
först här anropas funktionera för till exempel förflyttning av spelaren.
Ett exempel på händelsehanteraring i projektet är funktionen spray(), där musrörelser skulle användas
för att spelaren skulle måla sin ”tag” på måltavlorna. Då SDL sedan tidigare initierats genom
SDL_Init(SDL_INIT_EVERYTHING);
kunde SDL:s inbyggda händelsehantering hänvisas till genom
SDL_event e;
och funktionerna fanns tillgängliga. Implementationen kunde sedan ske smidigt med t.ex.
if (e.type == SDL_MOUSEBUTTONDOWN){
printf("Mouse button %d pressed at (%d,%d)\n", e.button.button, e.button.x,
e.button.y);
mouseclicked = 1;
}
där mouseclicked = 1 användes som en flagga som skulle signalera att användaren kunde få
instruktioner om att utföra en viss musrörelse. Om rörelsen utfördes korrekt ledde händelsen vidare till
att måltavlan fick spelarens färg.
SDL:s inbyggda funktioner för händelsehantering användes även när tangentbordstryckningarna skulle
tolkas så att spelaren rörde sig åt ett visst håll: vänster, höger eller uppåt (hopp). För att åstadkomma
detta utnyttjades SDLK_XX där ändelsen markerade vilken tangent som tryckts ned. Funktionen
därmed skrivas
if (keystates[SDLK_LEFT] == 1){
isgoing = LEFT;
}
if (keystates[SDLK_RIGHT] == 1){
isgoing = RIGHT;
}
if(keystates[SDLK_LEFT] == 0 && keystates[SDLK_RIGHT] == 0){
isgoing = 0;
}
if (keystates[SDLK_UP] == 1){
jumpkeypressed = 1;
}
else if (keystates[SDLK_UP] == 0) {
jumpkeypressed = 0;
}
där isgoing användes som en flagga för att signalera till applikationen att spelarfiguren skulle fortsätta
att röra sig åt höger. På samma sätt användes jumpkeypressed för att signalera att ”hopp-knappen” var
intryckt.
Det hade också varit möjligt att mitt under eventloopen direkt hantera varje händelse. Istället för att
bara sätta en flagga skulle funktioner kunna anropas direkt härifrån; i extremfallet skulle hela
funktionaliteten ligga här. Förmodligen är skillnaderna för programmets exekvering här väldigt små på
dagens datorer och processorer, men det känns inte som en god idé att låsa fast processorn i en
händelsehantering så att den därmed inte direkt är redo att lyssna efter nästa händelse. Kanske hade det
varit möjligt att ha en separat tråd för händelsehanteringen, som kördes parallellt med spelets
uppdateringscykel, men nuvarande lösning tycks fungera fullgott.
28(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
7.5 Spellogik
Spelets logik handlar till största delen om att läsa av flaggor som sätts av händelsehanteraren, och
sedan utföra olika instruktioner beroende på vilka av dessa som satts.
Under en uppdateringscykel hos klienten kommer spelet bland annat att kontrollera om spelaren
befinner sig framför en måltavla (vilket är ett krav för att funktionen spray() ska kunna anropas), om
spelaren kolliderar med någon plattform, och sedan alltså ytterligare kontroller och instruktioner
beroende på vilka knappar som är nedtryckta. Dessutom tickas diverse timers ned; bland annat för den
tid det tar att plocka upp nya sprayflaskor eller att spraya på en måltavla. Först när dessa timers når noll
kan spelaren förflytta karaktären igen.
Karaktären bromsas stegvis in om användaren släppt riktningstangenten, eller ökar hastighet om
tangenten fortfarande är nedtryckt.
Under uppdateringscykeln kommer slutligen ny information att skickas till servern, samtidigt som
information om andra förändringar kommer att tas emot.
29(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
30(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
8 Slutsats
Resultatet av projektet blev ett spel med 2D-grafik som går att spela över nätverk. Genom att använda
sig av SDL:s plattformsoberoende bibliotek kunde gruppen skapa grafik, händelsehantering,
nätverksfunktionalitet m.m. på ett enkelt sätt. Spelet spelas genom att klienten ansluter till servern och
väntar på att en spelomgång ska börja. När upp till fyra spelare markerat klartecken för att starta, börjar
spelet. Spelaren kan då med hjälp av piltangenterna röra sig över en bana som innehåller ett antal
objekt som går att måla på. Genom att utföra instruktioner med hjälp av tangentbord och mus, målar
spelaren sin “tag” över objekten. Vissa objekt nås endast genom att spelaren hoppar upp på särskilda
plattformar med hjälp av uppåt-tangenten.
En spelomgång tar 2 minuter, och tiden visas genom en timer längst upp i bild. När spelomgången är
över räknas poängen ihop, och den spelare som fått störst andel av objekten målade med sin ”tag”
vinner.
Klienten är plattformsoberoende och tar emot kommandon från användaren, information från servern
om spelarpositioner och tillståndet för spelplanen, och ritar upp grafik på skärmen. Det stora arbetet
sker således på klientsidan dels med hjälp av statisk information (bilder, typsnitt) lagrade hos klienten
och dels med hjälp av den dynamiska informationen som skickas från servern.
Servern är även den plattformsoberoende och har som huvuduppgift att, i tur och ordning:
1. Ta emot anslutningar från klienter och stoppa de som ej får plats
2. Initiera spelarlobbyn som klienten möts av innan spelet startar
3. Starta spelet och hantera inkommande information som t.ex. spelarpositioner, vilka mål som har
tagits av respektive spelare och annan nödvändig information, samt att skicka ut denna
information till spelarna så att de kan se vad som händer på planen
4. Avsluta spelomgången när tiden är slut
8.1 Projektarbetets fortskridande
Arbetsmetoden Extreme Programming användes för att effektivisera programmeringsarbetet och öka
samarbetet mellan gruppens deltagare. Metoden fungerade väl med hänsyn till att det för
gruppdeltagarna var första gången denna metod användes. Programspråket C lämpade sig väl för
uppgiften och kodorganisering med hjälp av header-filer och uppdelning i moduler visade sig vara till
stor hjälp i arbetet.
En annan stor bidragande faktor för effektiviteten i projektet var användandet av versionhantering med
hjälp av SVN. Genom att använda olika SVN-klienter kunde gruppens medlemmar arbeta samtidigt
med koden och hantera de olika kodversionerna enkelt och smidigt.
För gruppmedlemmarna var det första gången som ett IDE krävdes i arbetet, och ett antal problem
uppstod på vägen på grund av brist på erfarenhet och vissa oförutsedda plattformsskillnader, främst
relaterade till de olika SDL-bibliotek som användes. Emellertid kunde dessa problem lösas på vägen
och i slutet av projektet löpte arbetet på utan problem.
I början av projektet genomfördes en stor faktainsamling kring grafiken, med fokus på de två
31(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
alternativen OpenGL och SDL. Inledningsvis användes OpenGL, men gruppen gick snabbt över till
SDL då det visade sig vara enklare att hantera och innehålla de relativt basala funktioner som behövdes
för projektet.
Det slutgiltiga programmet når upp till de mål som gruppen satte vid projektets inledning. Gruppen
hade satt en deadline för en fungerande version med utvalda basfunktioner den 7 maj, och detta mål
uppfylldes flera dagar innan deadline. Efter denna deadline fortsatte gruppen med utvecklingen för att
uppfylla samtliga mål.
Moduldesignen som formulerades i början av projektet var mycket utförlig och hjälpte gruppen mycket
under utvecklingsarbetet. I slutfasen av projektet delades emellertid en stor del av modulerna upp för
att kunna anpassa programmet till kraven på bl.a. ett minimum av globala variabler. Detta medförde att
moduldesignen i sista stund blev ogiltig, men gruppen valde ändå att inte skriva någon ny då projektet i
stort ändå innehöll samma funktionalitet som i de från början stora modulerna, om än uppdelade i flera,
mindre, moduler.
Även det mycket utförliga kommunikationsprotokoll som upprättades i början av projektet blev efter
hand ändrat, men var till stor nytta under första hälften av projektet. Dokumentet gav en god överblick
över vad som skulle göras och vilka delar som behövdes för att klienten och servern skulle kunna
kommunicera.
8.2 Framtida arbete / Saker som inte hanns med
I slutresultatet fungerar det mesta felfritt; dock har vi bland annat stött på problem när vi försökt köra
vår klient på lite svagare datorer. I en så förhållandevis simpel applikation som vår är i jämförelse med
dagens avancerade spel, så borde någon vidare hög prestanda inte krävas. Det är oklart vad problemen
beror på, men de artar sig på så vis att klienten då har problem med att registrera musklick och
musrörelser; och att det därmed inte alltid går att spraya på targets.
En annan stor brist är att det inte går att välja vilken ip-adress man ska ansluta till. Spelet är därför låst
till vår egen dedikerade server. Då vi försökt implementera val av ip-adress och portnummer har de
märkligaste fel letat sig in i vårt program, varför vi valt att inte ha kvar denna funktion i den slutliga
versionen. Som en följd av detta går det inte heller att välja portnummer för servern; då den endast kör
på vår dedikerade maskin.
Vidare så är det i dagsläget kanske lite väl fritt för vilka klienter som helst att ansluta sig till klienten.
Någon form av kontroll skulle kanske behövas här, för att verifiera klientens äkthet. Det är även möjligt
att ansluta med klienter med helt olika egenskaper. Till exempel är det möjligt att alla klienter har helt
olika textfiler för att initiera plattformar och måltavlor. Det hade kanske varit önskvärt att kunna dela
med sig av kartor över spelet via servern.
Önskvärt hade också varit att kanske kunna välja bana innan spelet startade, samt att kunna göra till
exempel inställning för hur en spelomgång skall vara.
En renodlad lobby, med möjlighet att chatta och kanske välja karaktär, stod också på önskelistan.
Slutligen hade vi tänkt ha en snyggare Game Over-skärm, där resultatet presenterades på ett prydligare
sätt, och där någon form av ”slutsekvens” spelades upp i bakgrunden; till exempel att polisen dök upp
och att det var därför spelomgången tog slut. Det var även tal om att lägga in påskägg om spelarna
32(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
tillsammans uppnådde en viss mängd använda sprayflaskor: nämligen att ozonlagret till slut gav vika
och att hela staden sattes i brand.
33(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
34(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
9 Ordförklaringar
Ord
Förklaring
SDL, Simple DirectMedia Layer
Multimediabibliotek skrivet i C. Arbetar på lågnivå
och tillhandahåller ett lätthanterligt interface för
grafik, ljud och i/o-enhter för flera olika
plattformar.
OpenGL, Open Graphics Library
Plattformsoberoende API för att skriva
applikationer med datorgrafik i 2D eller 3D.
SVN, Subversion/Apache Subversion
Ett versionshanteringssystem som håller reda på
historik och förändringar av filer. Gör det möjligt
att gå tillbaka till en gammal version av kod eller
dokument. Används även som en slags logg.
TCP, UDP
Transmission Control Protocol och User Datagram
Protocol, två stycken olika protokoll för att skicka
data över ett nätverk. TCP är den säkra men slöa
varianten, och UDP den snabba men osäkra
varianten. Med säkerhet så handlar det om paketen
som skickas. TCP ser till att paketen kommer fram
och monteras ihop i rätt ordning. UDP har inte nån
form av felhantering. Det tar emot data i den
ordning som den kommer, vilket kan orsaka
problem.
IDE, Integrated Development Environment
Ett program eller en programsvit som vanligtvis
innehåller en textredigerare, kompilator, och
debugger, tillsammans med ett antal andra
funktioner avsedda att underlätta vid
programmering.
Header-filer (kodorganisering)
Kod som ska organiseras i moduler delas upp i
en .c-fil, innehållandes implementationen av koden,
samt en .h-fil (header-fil), som kan betraktas som
modulens publika gränssnitt gentemot övrig kod.
Xtreme Programming
Arbetsmetod som innebär att utvecklingsarbetet är
en iterativ process, med kontinuerlig testning av
applikationens moduler.
35(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
36(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
10 Referenser
Simple DirectMedia Layer
http://www.libsdl.org
SDL Wiki
http://www.libsdl.org/cgi/docwiki.cgi
NeHe Productions
http://nehe.gamedev.net
SDL:Tutorials:Setup
http://gpwiki.org/index.php/C:How_to_set_up_your_SDL_Build_Environment
Versionhantering (Wikipedia)
http://sv.wikipedia.org/wiki/Versionshantering
Extreme Programming (Wikipedia)
http://en.wikipedia.org/wiki/Extreme_Programming
The C book – Pointers
http://publications.gbdirect.co.uk/c_book/chapter5/pointers.html
SDL_net 1.2
http://www.libsdl.org/projects/SDL_net/
Simple Directmedia Layer (Wikipedia)
http://en.wikipedia.org/wiki/Simple_DirectMedia_Layer
Cisco Internetworking Technology Handbook
http://www.cisco.com/en/US/docs/internetworking/technology/handbook/Internet-Protocols.html
Utvecklingsmiljöer (pdf) (Anders Lindström)
Versionhantering (Wikipedia)
http://sv.wikipedia.org/wiki/Versionshantering
Wikipedia:
http://en.wikipedia.org/wiki/C_standard_library
37(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Wikipedia
http://sv.wikipedia.org/wiki/Objektorienterad_programmering
”C Genom ett nyckelhål” (Håkan Strömberg)
38(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Appendix A: Moduldesign
Moduler på serversidan
Serverapplikationen ska ta emot data från enskilda anslutna klienter, göra poängberäkningar samt
skicka dessa data till övriga klienter. Det handlar framförallt om spelarpositioner, måltavlornas färger
och en procentberäkning av vilka färger som dominerar bland måltavlorna. Servern kommer att köras
på en Linuxdator och använda sig av trådar och sockets för att hantera varje enskild klient.
Server.c/Server.h

Inkluderar ”Targets.h”, “Players.h”, “GameSession.h”

Har funktionen main()

Initierar, startar upp en eventloop och ser sedan till att avsluta programmet.
GameSession.c/GameSession.h

Har hand om att starta en spelomgång. När klienter anslutit till servern men inte startat spelet
ännu, visas en meny med antal anslutna spelare samt en möjlighet att starta spelet. En timer
kommer då att starta, och när den nått noll är spelomgången slut.
ConnectionHandler.c/ConnectionHandler.h (Engine.c)

Inkluderar <socket.h>, ”Players.h”, ”Targets.h”

Har funktionerna handleClient(), forwardPositions(), forwardTargets(), forwardPowerups()

Har hand om att etablera nya anslutningar. När en klient ansluter sker följande:
◦ En ny socket skapas, exklusivt för kommunikationen med den anslutna klienten
◦ En ny tråd skapas. Tråden tar en socket som argument, kör sedan handleClient().
▪ handleClient() tar en socket som argument, och är representationen av en klient på
servern. Funktionen har ett antal lokala variabler, som varje tråd alltså har en egen
uppsättning av:

socket, används för att skicka och ta emot data

playerPosition, läser in spelarens position. Skickas sedan vidare till övriga klienter
med anropet forwardPositions(playerPosition)

targetID, läser in vilken måltavla spelaren just tagit så att övriga klienter kan få reda
på dess nya färg. Skickas sedan vidare till övriga klienter med anropet
forwardTargets(targetID)

powerupID, läser in vilken power-up som just plockades upp så att övriga klienter
får reda på att den försvunnit från banan. Skickas sedan vidare med
forwardPowerups
▪ handleClient() körs om och om igen i en while(true)-loop, tills klienten väljer att avbryta
anslutningen.
39(57)
Graffiti Wars

Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
ConnectionHandler() har ett antal gemensamma resurser, som den använder för att skicka data
till klienter.
◦ En array med befintliga sockets/anslutna klienter.
◦ En poängberäkning av hur mycket av den totala ytan varje lag/färg just nu täcker
◦ En array med alla måltavlors nuvarande färg
◦ En array med varje spelares nuvarande position
◦ En array med vilka powerups som finns kvar på banan
Moduler på klientsidan
Klientapplikationens uppgift är att hantera den enskilde spelaren och världen han eller hon rör sig i;
med avseende på både händelsehantering och grafik. Bland klientens uppgifter ingår också att ta emot
och skicka data till servern.
Klienten ska gå att kompilera för användning på både Windows- och Linuxdatorer, och det skall
därmed också vara möjligt att spela med varandra oberoende av plattform.
Klientapplikationen kommer att ha ett huvudprogram, client.c, som i sin tur använder sig av några
specifika moduler.
Client.c/Client.h

Inkluderar ”Eventhandler.h”, ”Player.h”, ”Targets.h”, ”Obstacles.h”, ”Map.h”

Har funktionen main()
◦ Initierar, startar upp en eventloop och ser sedan till att avsluta programmet.
Player.c/Player.h

Har funktionerna movePlayer(), jumpPlayer(), setCans(), setSpeed(), getSpeed(),
getWidth(), getHeight(), getColor(), getImage()
Targets.c/Targets.h

Har funktionerna setColor(), getColor(), getWidth(), getHeight()

Representerar en ”måltavla” som går att klottra på, t.ex. en anslagstavla eller staty.
Map.c/Map.h

Inkluderar ”Targets.h”, ”Obstacles.h”, <stdlib.h>

Har funktionerna getBackground(), getObstacles(), getTargets()
(som Graphics.c behöver känna till)

Läser in från textfil vilken bakgrund som ska användas samt var plattformarna och
måltavlorna ska finnas.
Eventhandler.c/Eventhandler.h
40(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl

Inkluderar ”Player.h”, ”Targets.h”, ”Graphics.h”

Har bland annat funktionerna eventLoop(), eventTimer(), handleEvents() och refresh()
◦ eventLoop() innehåller en while(true)-loop som ständigt läser av event-kön
◦ eventTimer() kommer att generera ett så kallat ”user event” med jämna mellanrum
(förmodligen 30 gånger per sekund) vars syfte bland annat är att uppdatera skärmen.
◦ handleEvents() tar hand om händelserna i programmet. Där ingår både en timergenererad händelse som ser till att programmet uppdateras med jämna intervall samt
användarens knapptryckningar. Vid respektive händelse anropas tillhörande funktioner.
▪ refresh() anropas med jämna mellanrum vid Timer-events

Uppdaterar grafiken (genom modulen Graphics.c)

Tar emot data från och skickar data till servern:
◦ Skickar spelarens position till servern
◦ Tar emot andra spelares positioner från servern
◦ Skickar id-nummer på vilken måltavla spelaren just klottrat på till servern
◦ Tar emot färginformation om måltavlor från servern
▪ move() anropas vid tryck på vänster/höger piltangent eller A/D

Uppdaterar spelarens position (genom modulen Player.c)
▪ jump() anropas vid tryck på SPACE-tangenten

Ändrar spelarens position i y-led (genom modulen Player.c)

Har kollisionshantering för att se till att spelaren landar igen när den kolliderar
med marken eller med en plattform
▪ spray() anropas då spelaren befinner sig vid en måltavla och klickar på musen

Kontrollerar om användaren befinner sig vid en måltavla; detta genom att
jämföra spelarens och måltavlornas positioner. Endast då är det möjligt för
spelaren att klicka på musen för att inleda en klotterspelsekvens

Ber spelaren att föra musen i en bestämd riktning. Vid framgång byter måltavlan
färg, annars inte

Normalt krävs det att spelaren klarar tre musförflyttningar

Ett sprayförsök (lyckat eller misslyckat) kostar en sprayburk
▪ restock() anropas då spelaren åker till sprayburkbussen för påfyllning

Fyller på spelarens sprayburkar (genom Player.c)
▪ powerUp() anropas då spelaren tar upp en power-up
41(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl

Slumpen avgör vilken förmåga spelaren får
◦ Ökar spelarens åkhastighet (tidsbegränsad)
◦ Ökar spelarens sprayhastighet. Ändrar så att spelaren endast behöver klara en
musförflyttning vid klottring, istället för tre (tidsbegränsad)
◦ Möjliggör att spelaren trycker på ENTER för att spraya på näraliggande
motspelare (endast en attack per power-up)
Graphics.c/Graphics.h

Inkluderar ”Map.h”, ”Obstacles.h”, ”Player.h”, ”Targets.h”

Har bland annat funktionerna clearScreen(), refreshScreen()

Kallar på get()-funktioner i Map, Obstacles, Player och Targets för att få veta positioner, bredd
och höjd för de objekt som ska ritas upp på skärmen.
42(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Appendix B: Kravspecifikation
Denna kravspecifikation gäller projektarbetet i kursen Datortekniskt projekt på KTH STH. Produkten
som ska utvecklas går under arbetsnamnet ”Graffiti Wars”, och är ett spel i 2D-miljö som går ut på att
”klottra” på en yta med andra medspelare samtidigt i bild. Varje spelare har en egen ”tag” och kan
klottra över andras målningar. Spelet ska drivas med hjälp av en server som ska kunna hantera upp till
4 st. klienter samtidigt.
Funktionskrav
Spelet ska innehålla följande funktioner:
 En gemensam yta där spelarna klottrar
 Möjlighet att måla endast på vissa ytor
 Sidoscrollning
 Spelare ska ej kunna krocka
 Taggning genom individuella knappkombinationer
 Möjlighet att hoppa upp på plattformar och tagga där
 Powerups (objekt som ger spelaren fördelar som t.ex. högre hastighet)
 4st. spelare, själva eller i 2st. lag, vilket innebär att spelarnas skärmar kan se olika ut beroende
på var spelaren befinner sig
 Grafiskt gränssnitt
 Ska gå att spela över nätverk
Icke-funktionella krav
Produkten har följande icke-funktionella krav:
 Ska gå att köra i både Linux- och Windowsmiljö
 Programmerat i C med hjälp av funktioner och moduler
 Utveckling i Eclipse IDE
 Skriven i C
 Ska drivas med hjälp av en server i UNIX/Linux-miljö som kan hantera parallell
 programmering/trådar
 Nätverksfunktionalitet med hjälp av TCP-protokoll på applikationsnivån
 Utvecklas med hjälp av projektmetoden Extreme Programming, vilket medför att testning av
enskilda moduler ska ske parallellt med att produkten utvecklas
Produktionskrav
Spelet ska utvecklas i KTH STH Campus Haninge, med hjälp av egen server i rum 7070.
43(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Dokumentationskrav
Rapportskrivning ska ske parallellt med att projektet genomförs. I slutet av projektet lämnas en slutlig
rapport in. I övrigt gäller följande dokumentationskrav:
 Status- och tidsrapporter samt tidsplan publiceras kontinuerligt på Bilda
Tidskrav
Följande deadlines ska efterföljas i projektet:
DL1: 28/4 Halvtidsseminarium – muntlig presentation av arbetet för examinator
DL2: 12/5 Rapporten lämnas till handledaren
DL3: 19/5 Slutlig version av rapporten klar
DL4: 24 och 25/5 Slutseminarium med opposition
44(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Appendix C: Kommunikationsprotokoll
Information som ska skickas mellan server och klient
Varje tråd på serversidan ska från sin klient ta emot:
id-nummer för upplockade power-ups
id-nummer för klottrade objekt
spelarens positioner
Därefter ska servern skicka denna information till övriga trådar som skickar det vidare genom
sina sockets, så att alla klienter får ta del av den uppdaterade datan. Nya positioner och idnummer lagras i arrayer som samtliga trådar har tillgång till.
Följande gemensamma arrayer behövs:
Socket clients[4], där sockets lagras för kommunikation till och från klienterna.
Pthread_t threads[4], där trådarna lagras för att senare kunna avslutas på rätt sätt.
int targets[10], där index motsvarar måltavlans id-nummer och där innehållet är en färg. Till
exempel ändras den första måltavlan genom targets[0] = 1 för att få färgen representerad av intvärdet 1.
int powerups[5], där index motsvarar föremålets id-nummer och där innehållet är antingen 1 för
att måltavlan finns kvar på banan eller 0 för att måltavlan är borttagen från banan.
int playerposition[4][2] där första fältet anger id för spelaren (ett värde mellan 0 och 3), och där
det andra värdet anger spelarens position i x- och y-led. Till exempel betyder
”playerposition[1][0] = 20, playerposition[1][1] = 5” att spelare #2 har koordinaterna (20,5).
Huvudtrådens uppgifter på serversidan
Kör en while(true)-loop som gör följande:
1. Avslutar färdiga trådar och sockets och ta bort dem ur sina respektive arrayer
2. Lyssnar efter nya anslutningar. När en ny anslutning upprättats sker följande:
i. Servern sätter upp en socket för kommunikation med den anslutna klienten. Socketen lagras i
första lediga position i clients[]-arrayen. Den lediga positionen blir klientens
clientID. Ledig position eftersöks genom en for-loop.
ii. Servern skapar omedelbart en ny tråd, där den anropar funktionen handleClient() med den
nya socketen som argument; t.ex. handleClient(clients[0], 0). Tråden lagras i
threads[]-arrayen.
45(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Huvudtrådens uppgifter på klientsidan
1. Upprätta en anslutning till en server på en given adress med ett givet portnummer
2. Hantera användarens inmatningar och rita upp grafik tills användaren avslutar.
3. Skicka ett meddelande till servern om att klienten gjort sitt.
46(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Servertrådarnas uppgifter
1. Servern skickar ett välkomstmeddelande till klienten.
2. Servern tar emot en hälsning från klienten. Därefter inleds en while(true-loop):
3. Ta emot data från klienten via TCP
i. Undersök om det finns information att ta emot via TCP
receive = <TCPSOCKET>
if (receive == 1) // Det finns ett targetID att ta emot
targetID = <TCPSOCKET>
target[targetID] = clientID; // ändrar till nuvarande klients färg
else if (receive == 2) // Det finns ett powerupID att ta emot
powerupID = <TCPSOCKET>
powerups[powerupID] = 0; // tar bort motsvarande powerup
else
ta inte emot något via TCP
ii. Servern kollar om klienten har valt att avsluta och avslutar isåfall while-loopen
quit = <TCPSOCKET>;
4. Skicka data till klienten via TCP
i. print <TCPSOCKET> targets[] – skicka hela targets[]-arrayen
ii. print <TCPSOCKET> powerups[] – skicka hela powerups[]-arrayen
5. Ta emot data från klienten via UDP
i. String playerposition = <UDPSOCKET>; // tar emot en sträng på formatet ”x,y”
ii. Hantera strängen för att få ut x- och y-koordinaten och sätt in
playerposition[clientID][0] = x-koordinaten
playerposition[clientID][1] = y-koordinaten
6. Skicka data till klienten via UDP
i. print <UDPSOCKET> playerpositions[][] – skicka hela playerpositions[][]-arrayen
47(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Klientens uppgifter
1. Klienten tar emot ett välkomstmeddelande från servern.
2. Klienten skickar en hälsning till servern. Därefter inleds en while(true)-loop:
3. Skicka data till servern över TCP
i. Undersöker om det finns information om sprayad måltavla att skicka över TCP:
if (sprayedobject)
print <TCPSOCKET> 1; // talar om för servern att ett targetID ska skickas
print <TCPSOCKET> targetID; // skickar sedan måltavlans id-nummer
else
print <TCPSOCKET> 0; // talar om för servern att ingenting behöver
skickas
ii. Undersöker om det finns information om upptagen powerup att skicka över TCP:
if (poweruptaken)
print <TCPSOCKET> 2; // talar om för servern att ett powerupID ska
skickas
print <TCPSOCKET> powerupID // skickar powerupens id
else
print <TCPSOCKET> 0; // talar om för servern att ingenting behöver
skickas
iii. Skickar meddelande till servern om att klienten vill avsluta
print <TCPSOCKET> quit // skicka begäran om att avsluta (1=avsluta, 0=fortsätt köra)
4. Ta emot data från servern över TCP
i. targets[] = <TCPSOCKET> // ta emot alla måltavlors färger
ii. powerups[] = <TCPSOCKET> // ta emot alla powerups status (1=finns, 0=finns inte)
5. Skicka data till servern över UDP
o Print <UDPSOCKET> player.pos // skicka spelarposition som en sträng på formatet x,y
6. Ta emot data från servern över UDP
o playerpositions[][] = <UDPSOCKET> // ta emot alla spelares positioner
48(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Appendix D: Användarmanual
Användarmanual
Denna sektion innehåller en manual för klienten och servern. Båda programvarorna går att köras i
Linux- eller Windowsmiljö.
Köra Klienten
Klienten startas genom att köra filen GraffitiWarsClient.exe. Spelaren kommer då in i lobbyn och kan
där trycka på Space för att markera sin vilja att starta ett spel. När upp till fyra spelare har anslutit till
lobbyn kan vem som helst av dessa trycka på Enter för att starta.
Spelet inleds med alla spelare längst till vänster på spelplanen. Spelaren styr därefter sin karaktär på
följande sätt:
Tangent/musklick
Funktion
Pil höger
Styr åt höger
Pil vänster
Styr åt vänster
Pil upp
Hoppa
Musklick vänsterknapp
Utföres vid en måltavla. Spelaren får då upp en
instruktion om hur denne ska måla sin ”tag” på
tavlan.
Musrörelsen höger/vänster
Utföres vid en måltavla. Spelaren för muspekaren
åt det håll som instruerats, för att måla på
måltavlan.
Esc
Avslutar spelet direkt
Tabell 4: Användarinstruktioner tangentbord/mus
Spelaren rör sin karaktär över spelplanen med hjälp av tangentbordet (se Tabell 4:
Användarinstruktioner tangentbord/mus). När spelaren står vid en måltavla kan denne trycka en gång
på musen (vänster musknapp) och får då upp en instruktion om åt vilket håll muspekaren ska föras för
att måltavlan ska sprayas med spelarens färg (grön, blå, röd eller gul). Knappen måste hållas inne
medan rörelsen med pekaren utförs för att målningen ska lyckas.
En målning ”kostar” tre sprayburkar. När spelaren inte kan måla får denne upp ett meddelande på
skärmen (”Not enough cans!”).
Powerups
På utvalda platser på spelplanen finns s.k. ”Powerups” där spelarna kan fylla på sprayburkar eller få
högre rörelsehastighet.
49(57)
Graffiti Wars
Symbol
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Benämning/Funktion
More speed!
Spelarens rörelsehastighet blir högre under en
begränsad tid.
Cans restocked!
Spelarens förråd av sprayburkar fylles på till 10
(max antal)
Tabell 5: Tillgängliga powerups
Spelaren får del av powerupen så fort denne rör sig över symbolen (se Tabell 5: Tillgängliga
powerups).
Spelets gång
Längst upp i bild finns tre olika siffervärden. Den första (längs till vänster) visar det antal procent av
måltavlorna som spelaren har sprayat sin ”tag” på just nu. Siffran i mitten visar timern som räknar ner
från 120 sekunder. Siffran längst till höger visar antalet sprayburkar som spelaren har kvar (max 10 st).
När timern nått 0 är spelomgången slut och poängen räknas ihop. Den spelare som då sprayat sin ”tag”
på högst andel måltavlor har vunnit. Spelarnas poäng presenteras på skärmen.
Skapa egna banor
Användarna kan skapa egna banor genom att ändra i filerna Obstacles.txt, Powerups.txt och Targets.txt.
Koordinaterna för respektive objekt ligger här lagrade ett objekt/rad i formen XXXX YYYY, t.ex. 0250
0430 (250 i x-led, 430 i y-led). Det går inte att lägga till eller ta bort objekt från filerna. Endast
objektens position (och i fallet Powerups.txt även objekttyperna 0 eller 1) kan ändras.
Köra servern
Servern startas genom att köra filen GraffitiWarsServer.exe. Intressant utdata visas i terminalfönstret
som t.ex. skickade spelarpositioner och andra händelser. Servern stängs genom att avsluta programmet.
Då avslutas även alla aktiva trådar och sockets.
Bygga i Windows
För att bygga applikationen i Windows krävs att man installerar följande bibliotek på datorn:
1. SDL
http://www.libsdl.org/release/SDL-devel-1.2.14-VC8.zip
2. SDL_net
http://www.libsdl.org/projects/SDL_net/release/SDL_net-devel-1.2.7-VC8.zip
3. SDL_image
http://www.libsdl.org/projects/SDL_image/release/SDL_image-devel-1.2.10-VC.zip
50(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
4. SDL_ttf
http://www.libsdl.org/projects/SDL_ttf/release/SDL_ttf-devel-2.0.9-VC8.zip
http://savannah.nongnu.org/download/freetype/
Under projektinställningarna går man till ”Linker”, där man kan lägga till ytterligare bibliotek som ska
användas vid kompilering. Dessa är de ovan nämnda; i Visual Studio skriver man dem såhär:
SDLmain.lib;SDL.lib;SDL_net.lib;SDL_image.lib;
I andra utvecklingsmiljöer, som Eclipse och Code::Blocks listar man istället följande
kommandoradsargument:
-lSDLmain
-lSDL
-lSDL_net
-lSDL_image
-lSDL_ttf
Man kommer även behöva specificera ”additional include directories” och ”additional library
directiories”. Här ska man ange sökvägen till de fyra bibliotekens, som man installerade ovan,
respektive /include- och /lib-underkataloger.
I Visual Studio går man in under projektegenskaperna och VC++ Directories, och anger som Include
Directories, förutsatt att sökvägen är C:\C\
C:\C\SDL_ttf-2.0.9\include;C:\C\SDL-1.2.14\include;C:\C\SDL_net-1.2.7\include;C:\C\SDL_image1.2.10\include
Som Library Directories anger man
C:\C\SDL_ttf-2.0.9\lib;C:\C\SDL-1.2.14\lib;C:\C\SDL_net-1.2.7\lib;C:\C\SDL_image-1.2.10\lib
I Visual Studio behöver man även gå in och stänga av inställningen “Precompiled Headers”, under
projektinställningarna och alternativet C/C++.
Slutligen ska följande .dll-filer finnas med i den katalog där ens c- och .exe-filer ligger:
libfreetype-6.dll
SDL.dll
SDL_image.dll
SDL_net.dll
SDL_ttf.dll
zlib1.dll
Dessa dll-filer hittar man i lib-mapparna hos respektive installerat bibliotek.
Bygga i Linux
För att bygga i Linux krävs att man installerar följande bibliotek:
1. libsdl-net1.2-dev
2. libsdl-ttf2.0-dev
51(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
3. libsdl-image1.2-dev
4. libsdl-gfx1.2-dev
5. libsdl1.2debian
6. libconio (för att kunna hantera getch()-anrop)
http://sourceforge.net/projects/libconio
I projektinställningarna i t.ex. Eclipse länkas, på samma sätt som i Windows, följande bibliotek:
•
SDLmain
•
SDL
•
SDL_net
•
SDL_image
•
SDL_ttf
Projektet är testat på kompilatorn GCC, men kan även stödjas av andra kompilatorer i Linux-miljö.
52(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Appendix E: Testning
Detta dokument sammanfattar de tester som gjorts för projektet Graffiti Wars. Testerna är uppdelade
efter de två metoderna ”Black box testing” (Fuzz testing, Boundary value) och ”White box testing”.
(Model based, Fault injection, Mutation testing).
Testtyperna ovan utfördes på dessa delapplikationer:
1
Test av klienten, kommunicerande med en testserver
2
Test av servern, kommunicerande med en testklient
3
Test av kommunikationen mellan servern och klienten
Efter att testerna utförts sammanfattades resultaten. Testerna och den resulterande datan (utskrifterna)
sammanfattas i tabeller nedan.
Test av klienten
Här testas klienten kommunicerandes med en testserver.
Black box
I Black box tester vet vi ej vad som händer inuti applikationen. Vi kan endast testa enligt principen Ge
indada → Applikationen gör sitt jobb → Observera utdata.
Model-based testing
Här vet vi modellen för programmet, men ej exakt hur koden ser ut eller hur den fungerar. Vi skapar
lämpliga test utifrån detta.
Test nr Testbeskrivning
1
Targetstring och playerstring skickas till klienten i fel ordning
Targetstring skickas där playerstring förut skickades, och targetstring där playerstring
skickades. Följden blir att objekten på spelplanen snabbt byter plats och blinkar till
snabbt. Spelomgången avslutas nästan direkt.
2
Max antal spelare sätts till 2 istället för 4
Klienten tar emot information för max 2 spelare istället för 4. Klienten avslutas då direkt
(går till Game Over).
3
Klienten tar inte emot någon signal om spelare som kopplats bort
Konsekvens: Spelare som avslutat spelet genom ”quit” (quit=atoi(&clientexit[0]) fortsätts
ändå att visas på skärmen för övriga spelare.
Fuzz testing
Vi testar klienten genom att skicka slumpdata över socketen:
Test nr Indata
Resultat
53(57)
Graffiti Wars
1
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Slumpmässiga tecken för
Inga powerups syns på skärmen.
powerups skickas över socketen.
Boundary value testing
Här testar vi olika gränser för indatan till klienten, t.ex. vad som händer om vi skickar negativa värden,
om vi skickar värden precis över en viss gräns, precis för få eller för många tecken i en sträng etc.
Test nr Indata
Förväntat resultat
1
1 tecken för stor playerstring
Segmenteringsfel (hamnar utanför array)
2
1 tecken för liten playerstring
Resultat:
Segmenteringsfel (hamnar utanför array)

Spelaren syns inte i bild (hamnar utanför banan).

Spelaren börjar i luften och spelet avslutas direkt (går direkt till Game Over med
poängsummering).
White box
I White box tester har vi full koll på vad som händer inuti applikationen. Vi kan således göra ändringar
i koden om vi vill.
Fault injection
Här försöker vi ”knäcka” klienten genom att skicka felaktiga indata från testservern.
Test nr Testbeskrivning
1
Tar emot felaktiga data i välkomstmeddelandet
Konsekvens: Fel ID sätts på deltagarna och endast en av karaktärerna kan röra på sig,
som sedan drar med sig alla andra.
2
Tar emot inkorrekt targetstring
Konsekvens: Klienten tar emot en felaktig sträng om måltavlorna. Inga av måltavlorna
färgas.
3
Tar emot en och samma targetstring
Konsekvens: Alla måltavlor färgas med samma färg.
4
Tar emot felaktig playerstring
Konsekvens: ???
5
Powerstring=00000 eller slumpdata i strängen
Konsekvens: Inga powerups visas på skärmen.
Mutation testing
Här kan vi ändra i koden för klienten för att se vad som händer om en viss kodsnutt byts ut. Här
exponeras eventuella svagheter och känsligheter i koden.
54(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Test nr Rad [xx] till [xx] i filen [xx.c]
Resultat
1
Bytte plats på tilldelning från rad 299 till
rad 300 i Communicate.c
Tilldelningen av variabeln self sker före id
tagits emot från servern; då kommer alla
klienter få samma id och effekten blir att
endast en klient kan styra, medan alla andra
spelare ”dras med” när den förflyttar sig
2
Bytte plats på tilldelning från rad 109 till
118 i Communicate.c
Förväntat: indexController initieras på fel sätt,
så att spelarpositionssträngen inte får några ykoordinater. Spelaren kommer då få
koordinaten y=0, och sväva i luften.
Faktiskt: Runtime error: ”stack around
variable strPlayerPosSend corrupted”
55(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
Test av servern
Här testas servern kommunicerandes med en testklient.
Black box
I Black box tester vet vi ej vad som händer inuti applikationen. Vi kan endast testa enligt principen Ge
indada → Applikationen gör sitt jobb → Observera utdata.
Model-based testing
Här vet vi modellen för programmet, men ej exakt hur koden ser ut eller hur den fungerar. Vi skapar
lämpliga test utifrån detta.
Test nr Testbeskrivning
1
Testklienten ändrades så att den tog emot positioner före nya skickades iväg. Då kommer
strängar av olika längd skickas i fel ordning, och servern kopplar ned klienten samtidigt
som felmeddelande utgår.
2
NUMBEROFPLAYERS sätts till 40 på servern. Då kommer spelet omedelbart att ta slut
och visa resultatskärmen, med märkliga tecken.
Fuzz testing
Vi testar servern genom att skicka slumpdata över socketen:
Test nr Indata
Resultat
1
ClientExit = 10 hos klienten
Denna int tolkas tydligen som en giltig char, och inget
oförutsett händer
2
ClientExit = ”10” hos klienten
Nu skickas en sträng av fel längd, vilket resulterar i att
ett tecken ”rinner över” till en annan sträng, och skapar
oväntat i alla strängar.
3
strPlayerPosSend = ”12220338” Spelaren kommer alltid ritas ut på en fast position
Boundary value testing
Här testar vi olika gränser för indatan till servern, t.ex. vad som händer om vi skickar negativa värden,
om vi skickar värden precis över en viss gräns, precis för få eller för många tecken i en sträng etc.
Test nr Indata
Förväntat resultat
1
pstrPlayerPosSend = ”1234”
Skickar för kort värde. Detta upptäcks av
servern, som avslutar kommunikationen med
klienten och kopplar ned denna.
2
pstrPlayerPosSend = ”12345678912…” För lång sträng skickas. Udda tecken hamnar på
udda platser i strängar. Även om klienten
56(57)
Graffiti Wars
Mikael Berg, Fan Ding, Oskar Lind, Robin Lindahl
avslutar och servern startar om, kvarstår de
konstiga tecknen i strängarna…
White box
I White box tester har vi full koll på vad som händer inuti applikationen. Vi kan således göra ändringar
i koden om vi vill.
Fault injection
Här försöker vi ”knäcka” servern genom att skicka felaktiga indata från testservern.
Test nr Testbeskrivning
1
Receive = 2 när ingen ytterligare data finns att skickas, då Receive-flaggan normalt ska
vara 0. Tvåan tolkas av servern som att den ska vänta på ytterligare att paket, som aldrig
kommer. Så småningom reagerar servern på detta och kopplar ned klienten.
2
Längden på receive, som ska skickas, sätts till 9 tecken längre. Servern kopplar då direkt
ner klienten.
3
Längden på receive, som ska skickas, sätts till 3 tecken kortare. Då väntar servern
fortfarande på ett längre paket, som aldrig kommer. Samma konsekvens som under test 1;
klienten kopplas ned.
Mutation testing
Här kan vi ändra i koden för servern för att se vad som händer om en viss kodsnutt byts ut. Här
exponeras eventuella svagheter och känsligheter i koden.
57(57)