Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Superwayne

Treue Seele

  • »Superwayne« ist der Autor dieses Themas

Beiträge: 242

Beruf: Student & App Entwickler (Xamarin)

  • Private Nachricht senden

1

15.02.2016, 22:21

[C#] Daten speichern/lesen mit GZipStream

Hallo,

ich versuche zurzeit Chunks für mein Spiel in einem Region File System zu speichern (ähnlich wie bei Minecraft, nach Anleitung von dieser Seite).

Das grundlegende Prinzip einmal zusammengefasst, damit nicht jeder den ganzen Dev-Blog lesen muss:
- 32*32 Chunks werden jeweils in einer Region Datei gespeichert.
- Am Anfang jeder Datei befindet sich eine Tabelle mit 32 * 32 Einträgen à 4 Byte.
- Die eigentlichen Daten werden in 256 Byte großen Sektoren gespeichert, wobei ein Chunk bei Bedarf über mehrere Sektoren geht.
- Die Position des Chunks in der Tabelle berechne ich mit y * 32 + x % 32.
- Die ersten 3 Byte eines Eintrags sind der Offset des ersten Sektors für die eigentlichen Daten des Chunks
- Das letzte Byte ist die Anzahl Sektoren, auf die die Daten verteilt sind.

Die Datei möchte ich jetzt mit Kompressionsalgorithmen klein halten, um die Lese-/Schreibdauer gering zu halten.
Mit RLE kann ich die einzelnen Chunkdaten schon einmal um ein ganzes Stück verkleinern. Jetzt würde ich aber gerne noch mit GZip die Gesamtdatei weiter komprimieren.
Dazu gibt es in C# die GZipStream-Klasse, ich verstehe allerdings nicht recht, wie ich diese am besten nutzen kann.
Muss ich mit dem GZipStream jedes mal die gesamte Datei auslesen, im Programmcode bearbeiten und dann komplett überschreiben? Das klingt nicht wirklich effizient. Momentan Springe ich am Anfang mit Seek zu den 4 Bytes in der Tabelle und lese diese aus. Mit den Daten daraus springe ich dann zum entsprechenden Sektor und lese die Sektordaten.

Kennt sich jemand damit aus und kann mir weiterhelfen, ob man den GZipStream einsetzen kann, ohne jedes mal die gesamte Datei auszulesen / zu überschreiben? Selbst wenn es kein Stream wäre, müsste ich doch jedes mal die komplette Datei auslesen und dekomprimieren, um an die Daten zu kommen, oder verstehe ich das falsch?

CeDoMain

Alter Hase

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

2

15.02.2016, 22:29

Minecraft komprimiert seine Dateien mehrmals - wenn ich mich korrekt erinnere. Und zwar werden die Chunks einzeln komprimiert, hintereinander gefügt und nochmals komprimiert. Wenn du nun was ändern möchtest, dann dekomprimierst du die "große" Komprimierung. Schaust in deine Tabelle, wo sich der komprimierte Chunk befindet und dekomprimierst diesen. Nach der Änderung wird der Chunk wieder komprimiert, an die korrekte Stelle eingefügt und eventuell die nachfolgenden Sektoren in der Tabelle angepasst (wenn dein Chunkcode größer geworden ist) und komrimierst die ganze Datei wieder.

Die ganze Datei zu komprimieren kannst du eventuell weglassen - dann musst du nur den Chunk dekomprimieren, den du verändern möchtest.

Nachtrag: Wenn du Änderungen erstmal im RAM speicherst und erst bei einer gewissen Menge an Änderungen die Datei aktualisierst, dann sparst du dir eine Menge Zeit. Sowas nennt sich Caching. Der Cache muss natürlich so klein gewählt werden, dass bei einem Absturz nicht zuviel verloren geht.
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

Superwayne

Treue Seele

  • »Superwayne« ist der Autor dieses Themas

Beiträge: 242

Beruf: Student & App Entwickler (Xamarin)

  • Private Nachricht senden

3

15.02.2016, 23:09

Die Chunks werden bereits gecached. Ich bin mir allerdings nicht sicher, was effizienter ist: Alles auslesen, Änderungen einpflegen, abspeichern oder die Änderungen einzeln in der Datei einpflegen. In einer Region Datei sind bis zu 1024 Chunks, um einen Spieler herum werden jedoch nur 16 Chunks geladen. Selbst bei 4 Spielern an 4 Orten in einer Region, wären das "nur" 64 von 1024 Chunks, die sich ändern. Ich habe mit der Performance der FileStream Klasse wenig Erfahrung und weiß nicht, wie Aufwendig es ist, wenn alle paar Minuten 64 einzelne Chunks geändert werden.

CeDoMain

Alter Hase

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

4

15.02.2016, 23:36

Das wird denke ich mal so keine Probleme verursachen. Du kannst ohne Probleme die gesamte Datei jede Minute neu schreiben. Wie viele MB ist die denn groß? Wenn du das ganze asyncron machst, dann sollte GZip keine Performanceeinbrüche verursachen, denn du musst eine sehr große Menge an Daten haben, um GZip länger als das Updateintervall (2 - 3 min) zu beschäftigen.
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

Superwayne

Treue Seele

  • »Superwayne« ist der Autor dieses Themas

Beiträge: 242

Beruf: Student & App Entwickler (Xamarin)

  • Private Nachricht senden

5

15.02.2016, 23:48

Aktuell wären es 1MB, da bisher nur die reinen Blöcke gespeichert werden. Dazu kommen dann irgendwann noch 2-3 weitere Ebenen (also nochmal 2-3 MB) und Monster, abgebaute Sachen, etc. Vermutlich nicht über 5MB insgesamt. Das Problem ist, dass das ganze ja im Netzwerk läuft. Wenn nun 4 Spieler in unterschiedlichen Teilen der Welt jeweils an einer Kreuzung von 4 Regionen herumlaufen, müssen bereits 16 Regionen immer wieder komplett gelesen/geschrieben werden. Wobei zumindest das Lesen vermutlich durch cachen reduziert werden kann.

CeDoMain

Alter Hase

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

6

15.02.2016, 23:52

Warum schickst du denn die gesamten Dateien über das Netzwerk? Schreib dir doch ein kleines Protokoll, was nur die Änderungen überträgt? Wenn zwei Clients gleichzeitig schreiben, dann gibts Zugriffsprobleme. Wenn du nur die Änderungen verschickst, dann können die viel Differenzierter behandelt werden.

Oder geht das jetzt an deinem Problem vorbei? :D
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

Superwayne

Treue Seele

  • »Superwayne« ist der Autor dieses Themas

Beiträge: 242

Beruf: Student & App Entwickler (Xamarin)

  • Private Nachricht senden

7

16.02.2016, 00:03

Nene, da habe ich mich falsch ausgedrückt. Die Clients fragen nur die Chunks an, die der Spieler gerade braucht. Der Server liest dann die entsprechende Datei und schickt die gebrauchten Chunks rüber. Danach werden nur noch Änderungen über das Netzwerk geschickt. Nur der Server muss die entsprechenden Dateien lesen/schreiben, wenn die Anfragen/Änderungen von den Clients kommen.

CeDoMain

Alter Hase

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

8

16.02.2016, 00:16

Ok, dann würde ich dir folgendes Empfehlen: Der Server cached eine bestimmte Anzahl (50) an Änderungen und schreibt die dann gleichzeitig in die Datei. Man kann zusätzlich noch festlegen, dass das jede Minute zwingend passieren muss. Die Datei selbst ist unkomprimiert - nur die Chunks in dieser sind komprimiert. Du kannst also beim öffnen die Chunks mithilfe einer Headertabelle genau anspringen und musst nur ihren Teil ändern. Die GZip-Klasses im .Net-Framework sind bei dieser Bytemenge auf jeden Fall ausreichend schnell. Mach es asyncron und du wirst nichts merken. :)

Um deine ursprüngliche Frage zu klären: Mitten in einen komprimierten Stream (z.B. Datei) zu schreiben ist bei GZip nicht möglich - entweder ganz oder garnicht. Daher Teile deine Datei wie beschrieben in kleine Abschnitte ein, die jeweils separat komprimiert werden.

Ich hoffe deine Frage ist nun geklärt! ;)
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

Superwayne

Treue Seele

  • »Superwayne« ist der Autor dieses Themas

Beiträge: 242

Beruf: Student & App Entwickler (Xamarin)

  • Private Nachricht senden

9

16.02.2016, 01:00

Ich hoffe deine Frage ist nun geklärt! ;)

Fast :D
Soweit ich verstanden habe, hat GZip ja immer einen gewissen Overhead und lohnt sich erst ab einer bestimmten Dateigröße. Wenn ich jetzt die 5kb großen Chunks einzeln mit Gzip verpacke, lohnt sich das doch gar nicht? :huh:

CeDoMain

Alter Hase

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

10

16.02.2016, 16:50

Nein, bei dieser Datenmenge lohnt sich eine GZip-Komprimierung überhaupt nicht. Meinst du, dass es immer bei dieser Datenmenge bleibt? Falls du später größere Datenmengen pro Voxel speicherst, kannst du ja immernoch auf GZip umsteigen. Selbst eine 6MB große Datei finde ich, kann unkomprimiert gespeichert werden, wenn sie nicht über ein Netzwerk übertragen wird.
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

Werbeanzeige