Ich wollte schon länger das hier machen, da es das in Java gibt, und dort einiges stark erleichtert. Jetzt bin ich endlich mal dazu gekommen.
Serialisierung bedeutet, ein Objekt so zu verpacken, dass es abspeicherbar oder verschickbar ist. Deserialisierung ist das Gegenstück dazu, wo ein verpacktes Objekt wieder entpackt wird, und dadurch verwendbar gemacht wird.
Dadurch kann man ein beliebiges Objekt mit nur einer Zeile Code verschicken/empfangen oder abspeichern/auslesen.
Für dieses Framework wird die Pointers-Userlib benötigt.
Was bringt diese Library?
Mit Hilfe dieser Library kann ein beliebiges Objekt mit gerade mal ein bis vier Zeilen Code (je nach benötigten Metadaten) abgespeichert oder über einen Internet-Stream verschickt werden. Das vereinfacht das Netzwerkprogrammieren sowie das Abspeichern enorm. Gleichzeitig packt die Libary die gegebenen Daten gut zusammen, was für einen niedrigeren Speicherbedarf sorgt.
Diese Funktionen kommen hierdurch dazu:
Code: [AUSKLAPPEN] [EINKLAPPEN] serialize(ptr%, format$, stream%)
Serialisiert ein Objekt und schreibt es in den gegebenen Stream hinein.
ptr% : Pointer auf das Objekt, das serialisiert werden soll (bekommt man mittels pointers_getPointer()
format$ : Hier sollten alle Felder des Objekts in der folgenden Syntax stehen:
b = byte
h = short
i = int
f = float
s = String
x = Feld wird ignoriert
stream$ : Ein File- oder Netzwerk-Stream in den geschrieben werden soll
deserialize(ptr%, format$, stream%)
Liest ein Objekt aus dem Stream aus und schreibt die Werte in das gegebene Objekt hinein.
ptr% : Pointer auf das Objekt, in deserialisiert werden soll (bekommt man mittels pointers_getPointer()
format$ : Muss das gleiche Format sein, wie beim Serialisieren
stream$ : Ein File- oder Netzwerk-Stream aus dem gelesen werden soll
Beispielcode für Speichern:
BlitzBasic: [AUSKLAPPEN] [EINKLAPPEN]
Include "serializer.bb"
Const TAnimalTypeNr = 1 Type TAnimal Field x# Field y# Field hp% Field image% Field species$ Field name$ End Type
Const THouseTypeNr = 2 Type THouse Field address$ Field number% Field height# End Type
Local dog1.TAnimal = New TAnimal dog1\x# = 75.3 dog1\y# = 400.2 dog1\hp = 10 dog1\species$ = "Dog" dog1\name$ = "Max" dog1\image = CreateImage(20,20)
Local dog2.TAnimal = New TAnimal dog2\x# = 12.3 dog2\y# = 46.7 dog2\hp = 80 dog2\species$ = "Dog" dog2\name$ = "Hasso" dog2\image = CreateImage(20,20)
Local cat.TAnimal = New TAnimal cat\x# = 78.5 cat\y# = 88.5 cat\hp = 5 cat\species$ = "Cat" cat\name$ = "Kitty" cat\image = CreateImage(20,20)
Local house1.THouse = New THouse house1\address = "Waldstrasse" house1\number = 82 house1\height = 12.5
Local house2.THouse = New THouse house2\address = "Dorfgasse" house2\number = 70 house2\height = 8.9
Print("Initial state:") printObjects() Print("") Print("Saving...") saveObjects("animals.sav") Print("Deleting...") deleteObjects() Print("") Print("Current state (everything deleted):") printObjects() Print("") Print("Loading...") loadObjects("animals.sav") Print("") Print("Current state (everything reloaded):") printObjects() Input("Press Enter to close...") End
Function saveObjects(path$) stream% = WriteFile(path$) For a.TAnimal = Each TAnimal WriteByte(stream%, TAnimalTypeNr) serialize(pointers_getPointer(a), "ffixss", stream%) Next For h.THouse = Each THouse WriteByte(stream%, THouseTypeNr) serialize(pointers_getPointer(h), "sif", stream%) Next CloseFile(stream%) End Function
Function deleteObjects() For a.TAnimal = Each TAnimal If (a\image<>0) Then FreeImage a\image Delete a Next For h.THouse = Each THouse Delete h Next End Function
Function loadObjects(path$) stream% = ReadFile(path$) While (Not Eof(stream%)) typeNr = ReadByte(stream%) If (typeNr=TAnimalTypeNr) Then Local a.TAnimal = New TAnimal deserialize(pointers_getPointer(a), "ffixss", stream%) ElseIf (typeNr=THouseTypeNr) Then Local h.THouse = New THouse deserialize(pointers_getPointer(h), "sif", stream%) EndIf Wend CloseFile(stream) End Function
Function printObjects() Print("") Print("##### Animals:") For a.TAnimal = Each TAnimal Print("### "+a\species$+" "+a\name$+" ###") Print("Pos: "+a\x#+"/"+a\y#) Print("HP : "+a\hp) Print("Image handle: "+a\image) Next Print("##### Houses:") For h.THouse = Each THouse Print("### "+h\address$+" "+h\number%+" ###") Print("Height: "+h\height#) Next End Function
Beispielcode für Netzwerkübertragung:
BlitzBasic: [AUSKLAPPEN] [EINKLAPPEN]
Include "serializer.bb"
Const WIDTH=800 Const HEIGHT=600
Const ISSERVER = False
Const SENDFREQ = 250
Const ENABLE_CLIENT_SIMULATION = True
Const ENABLE_FRAME_INDEPENDENCE = True
Graphics(WIDTH, HEIGHT)
Const TNetTextTypeNr = 1 Const NetActionCreate = 1 Const NetActionUpdate = 2 Const NetActionDelete = 3 Type TNetText Field x# Field y# Field xs# Field ys# Field txt$ End Type
Const MAX_TEXTS = 100 Dim texts.TNetText(MAX_TEXTS) Global textCt = 0
If (ISSERVER) Then runServer() Else runClient() EndIf
Function runServer() server% = CreateTCPServer(30000) stream% = 0 While (stream=0) Cls stream% = AcceptTCPStream(server) Text(WIDTH/2, HEIGHT/2, "Waiting for a client", True, True) Flip(1) Wend nextSend = MilliSecs()+SENDFREQ While (Not KeyHit(1)) Cls Text(5, 5, "Press left mouse button", False, False) If (MouseHit(1)) Then createNetText(MouseX(), MouseY(), Rnd(-20,20), Rnd(-20,20), "Text "+textCt, stream%) EndIf
updateTexts() drawTexts() If (nextSend<MilliSecs()) Then sendNetTexts(stream%) nextSend = MilliSecs()+SENDFREQ EndIf Flip(0) Wend End End Function
Function runClient() Cls Text(WIDTH/2, HEIGHT/2, "Connecting...", True, True) Flip(1) stream% = OpenTCPStream("127.0.0.1", 30000) If (stream%=0) Then Cls Text(WIDTH/2, HEIGHT/2, "Connection failed!", True, True) Text(WIDTH/2, HEIGHT/2+50, "Press any key", True, True) Flip(1) WaitKey End EndIf While (Not KeyHit(1)) Cls If (ENABLE_CLIENT_SIMULATION) Then updateTexts() EndIf receiveNetTexts(stream%) drawTexts() Flip(0) Wend End End Function
Global lastUpdateMs = -1 Function updateTexts() If (ENABLE_FRAME_INDEPENDENCE) Then If (lastUpdateMs<>-1) Then deltaT# = (MilliSecs()-lastUpdateMs)/1000.0 Else deltaT# = 1.0/60.0 EndIf lastUpdateMs = MilliSecs() For t.TNetText = Each TNetText t\x# = t\x#+t\xs#*deltaT# t\y# = t\y#+t\ys#*deltaT# t\x# = t\x# Mod WIDTH t\y# = t\y# Mod HEIGHT Next Else For t.TNetText = Each TNetText t\x# = t\x#+t\xs# t\y# = t\y#+t\ys# t\x# = t\x# Mod WIDTH t\y# = t\y# Mod HEIGHT Next EndIf End Function
Function drawTexts() For t.TNetText = Each TNetText Text(t\x#, t\y#, t\txt$, True, True) Next End Function
Function createNetText(x#, y#, xs#, ys#, txt$, stream%) If (textCt>=MAX_TEXTS) Then Return Local t.TNetText = New TNetText texts(textCt) = t t\x# = x# t\y# = y# t\xs# = xs# t\ys# = ys# t\txt$ = txt$ WriteByte(stream%, TNetTextTypeNr) WriteByte(stream%, NetActionCreate) WriteInt(stream, textCt) serialize(pointers_getPointer(t), "ffffs", stream%) textCt = textCt+1 End Function
Function sendNetTexts(stream%) For i=0 To textCt-1 WriteByte(stream%, TNetTextTypeNr) WriteByte(stream%, NetActionUpdate) WriteInt(stream, i) serialize(pointers_getPointer(texts(i)), "ffxxx", stream%) Next End Function
Function receiveNetTexts(stream%) While (ReadAvail(stream%)>0) typeNr = ReadByte(stream%) action = ReadByte(stream%) id = ReadInt(stream%) If (typeNr = TNetTextTypeNr) Then If (action = NetActionCreate) Then If (texts(id)=Null) Then texts(id) = New TNetText EndIf deserialize(pointers_getPointer(texts(id)), "ffffs", stream%) ElseIf (action = NetActionUpdate) Then If (texts(id)=Null) Then texts(id) = New TNetText EndIf deserialize(pointers_getPointer(texts(id)), "ffxxx", stream%) ElseIf (action = NetActionDelete And texts(id)<>Null) Delete texts(id) EndIf EndIf Wend End Function
Hier noch der Source-Code:
BlitzBasic: [AUSKLAPPEN] [EINKLAPPEN]
Include "pointers.bb"
Function serialize(ptr%, format$, stream%) For i=0 To Len(format)-1 token$ = Mid(format,i+1,1) If (token$="b") Then v = pointers_getInt(ptr + i*4) And 255 WriteByte(stream%,v) ElseIf (token$="h") Then v = pointers_getInt(ptr + i*4) And 65535 WriteShort(stream%,v) ElseIf (token$="i") Then v = pointers_getInt(ptr + i*4) WriteInt(stream%,v) ElseIf (token$="f") Then vf# = pointers_getFloat(ptr + i*4) WriteFloat(stream%,vf#) ElseIf (token$="s") Then vs$ = pointers_getString(ptr + i*4) WriteString(stream%,vs$) ElseIf (token$="x") Then Else DebugLog("Unexpected token while serializing: "+token$) EndIf Next End Function
Function deserialize(ptr%, format$, stream%) For i=0 To Len(format)-1 token$ = Mid(format,i+1,1) If (token$="b") Then v = ReadByte(stream%) pointers_setInt(ptr + i*4, v) ElseIf (token$="h") Then v = ReadShort(stream%) pointers_setInt(ptr + i*4, v) ElseIf (token$="i") Then v = ReadInt(stream%) pointers_setInt(ptr + i*4, v) ElseIf (token$="f") Then vf# = ReadFloat(stream%) pointers_setFloat(ptr + i*4, vf#) ElseIf (token$="s") Then vs$ = ReadString(stream%) pointers_setString(ptr + i*4, vs$) ElseIf (token$="x") Then Else DebugLog("Unexpected token while deserializing: "+token$) EndIf Next End Function
Und schlussendlich alles (inklusive der Pointers-Userlib) zusammen in einem Paket: *klick*
Wichtiger Hinweis:
Handles auf Bilder usw. dürfen nicht serialisiert werden, da sie auf einem anderen Computer / nach dem Speichern und Laden wohl nicht mehr an der gleichen Position liegen!
Edit: Habe das Netzwerk-Beispiel noch um Frameunabhängigkeit und Client-Side-Simulation erweitert, sowie die Senderate runtergeschraubt.
Update 11.07.2014:
-) Ein Update in der Pointers-Userlib hat Anpassungen im Serializer notwendig gemacht.
|