Das verstehen, Wie der Netcode funktioniert, macht deine Waffen / Gegenstände mit dem Multiplayer kompatibel, ohne eine Reihe von Tests durchführen zu müssen, und wenn du keine Freunde oder zwei Computer hast, Es kann stundenlanges unnötiges Debuggen und hoffnungslose Situationen ersparen.
P.s. diese Anleitung ist eine Übersetzung. Original: https://steamcommunity.com/sharedfiles/filedetails/?id=1394086869
Verbindungen und andere
Zuerst möchte ich kurz erklären, wie funktioniert netcode (Netzwerkcode), und in neueren Versionen ist es ziemlich kompliziert und verwendet "Geister" (Geister) zur Datensynchronisation, keine intensive Nutzung des Prozessors erforderlich. Aber das ist uns egal, da es Teil des Backends ist ("Interner Netzcode"), was deinen Code nicht beeinflusst.
Netcode besteht aus Verbindungen mit anderen Enten, und Sie können NetMessage senden (Netzwerknachrichten) oder spezifische Verbindungen, oder alle Verbindungen (normalerweise nur null als Verbindung übergeben).
Jedes Ding auf der Ebene gehört zu einer Verbindung, und der Besitzer dieser Verbindung sendet Eigenschaften zu der bestimmten Sache an alle anderen; also hat der Host/Server nicht alles unter Kontrolle, was meiner Meinung nach ein ziemlich häufiges Missverständnis ist. Zu Beginn des Levels kontrolliert der Host alles, abgesehen von anderen Enten.
Ich werde dies in diesen Teilen genauer erklären, wo benutzt du sie eigentlich?.
Zustandsbindung
public int myNumber = 25;
public StateBinding _number = new StaeBinding("myNumber",-1,false,false);
//это гарантирует автоматическую синхронизацию int myNumber.
Es gibt mehrere Überladungen für StateBinding, die meisten werden nie verwendet.
Main, welche du, wahrscheinlich, für fast alles verwenden (und die im ersten Beispiel verwendet wurde):
public StateBinding(string field, int bits = -1, bool rot = false, bool vel = false)
Bitwert – das ist die größe des feldes, das heißt, Int32 besteht aus 32 Seite. Wenn der Wert ist -1, die größe wird automatisch empfangen. Und das Sparen von ein paar Nanosekunden pro Spiel ist das mögliche Risiko eines Fehlers beim Wert nicht wert. Also lass es an -1. Sie können klarer sehen, wie es in der BitBuffer-Klasse funktioniert, mit einem Decompiler (z.B, DNSPy).
Die booleschen Werte rot und vel werden nur verwendet, wenn, wenn es um Rotationen und Geschwindigkeiten geht, und, wie ich es verstehe, sie werden nur bei der Berechnung der Physik verwendet. Ändere ihre Werte nicht auf true, wenn du es nicht weißt, was macht ihr.
Hier sind andere Überladungen, die du verwenden kannst (und was machen sie):
//изменяет значение постепенно вместо мгновенных скачков, работает только с числами с плавающей запятой.
public StateBinding(bool doLerp, string field, int bits = -1, bool rot = false, bool vel = false);
//наиболее специфичный конструктор statebind, работает как обычный + вы можете сделать его "плавным" и установить его важность (низкий, средний или высокий уровень). Использование данного конструктора может замедлить работу других вещей, поэтому вам не следует его использовать.
public StateBinding(GhostPriority p, string field, int bits = -1, bool rot = false, bool vel = false, bool doLerp = false)
- Schnur
- schweben
- doppelt
- Byte
- sbyte
- bool
- kurz
- ukurz
- int
- uint
- lange
- ulong
- verkohlen
- Vec2
- BitBuffer
- NetIndex16
- NetIndex2
- NetIndex4dex4
- NetIndex8
- Alles, was erbt oder ist Ding
StateBind funktioniert auch mit Eigenschaften. Ein Beispiel ist das, die in MegaLaser verwendet wird:
public StateBinding _frameBinding = new StateBinding("spriteFrame", -1, false, false);
public byte spriteFrame {
get {
if (this._chargeAnim == null)
return (byte) 0;
return (byte) this._chargeAnim._frame;
}
set {
if (this._chargeAnim == null)
return;
this._chargeAnim._frame = (int) value;
}
}
NetSoundEffects sind wie normale Sounds, und sie können auch beim Abspielen von Audio Funktionen auslösen. Außerdem kann ihre Höhe von einem anderen Wert abhängen.. Aber das Wichtigste, bei Verwendung mit NetSoundBinding werden sie online synchronisiert.
NetSoundEffects und NetSoundBinding werden verwendet, z.B, wenn die Ente quakt oder du die Trommel schlägst.
Sie können NetSoundEffect auf zwei Arten verwenden:
public StateBinding _netBounceSoundBinding = (StateBinding) new NetSoundBinding("_bounceSound "); //Statebinding для _bounceSound
public StateBinding _netYellSoundBinding = (StateBinding) new NetSoundBinding("_netYellSound "); //StateBinding для _netYellSound
public NetSoundEffect _bounceSound = new NetSoundEffect(); //Пустой SoundEffect, который будет привязан к функции.
public NetSoundEffect _netYellSound = new NetSoundEffect(new string[3]
{
"quackYell01",
"quackYell02",
"quackYell03"
}); //Создание нового NetSoundEffect, который, будучи единожды воспроизведён, запустит три новых звука, и его синхронизация, чтобы остальные игроки услышали новый звук.
public override void Initialize()
{
_bounceSound.function = new NetSoundEffect.Function(Bounce);
}
void Bounce()
{
SFX.Play(GetPath("myBounceSound.wav"));
shake = 5f;
}
Stellen Sie sicher, Was ist der Typ der Variablen? – Zustandsbindung, а не NetSoundBinding
Zustandsbindung (Fortsetzung)
StateFlagBinding ist sehr nützlich, wenn Sie es mit mehreren booleschen Werten zu tun haben, z.B, wenn etwas offen ist, geschlossen oder gesperrt.
Es ist nur eine schnellere Möglichkeit, mehrere logische Zustandsbindungen durchzuführen, was eigentlich ziemlich veraltet ist.
Parameter müssen bool sein, um verwendet zu werden, und dann werden ihre Werte zu einer ushort komprimiert. Die Verwendung von ushort bedeutet, dass du nicht mehr haben kannst 16 logische Felder.
public StateBinding _laserStateBinding = (StateBinding) new StateFlagBinding(new string[3]
{
"_charging",
"_fired",
"doBlast"
});
public bool doBlast;
public bool _fired;
public bool _charging;
Beispiel aus HugeLaser. Tatsächlich, Felder _charging und_fired werden darin nicht verwendet, und es hängt vom Sprite-Frame und dem Animationsindex ab, welche, wiederum, synchronisiert. Und so rufst du am besten an, andernfalls würde es zu einer Verzögerung zwischen allen Clients kommen. Außerdem, in Writing Proper Code funktioniert dies mit StateBindigs (Teile).
Arten von StateBindigs, was du nicht wissen musst, da sie nur zur Optimierung verwendet werden:
Datenbindung(Zeichenfolgenfeld) – Arbeitet effizienter mit BitBuffer, und nur mit ihnen.
CompressedVec2Binding – Reduziert die Vec2-Größe. Alle Konstruktoren haben Maximalwerte.
CompressedFloatBinding – Verringert die Schwimmergröße. Alle Konstruktoren haben Maximalwerte.
InterpolatedVec2Binding[/b] – CompressedFloatBinding-Erweiterung mit höchster Priorität.
[ha]So schreiben Sie den richtigen Code, was mit StateBindnds funktioniert.[/ha]
Dies ist besonders wichtig, wenn Sie mit Dingen wie Timern oder Werten zu tun haben, die sich schnell ändern. Im Grunde musst du deine Sachen so schreiben, als könnten sie zufällig eine Reihe von Zahlen überspringen und am Ende immer noch funktionieren work.
Das ist besonders wichtig, wenn Sie es mit Timern oder Werten zu tun haben, die sich schnell ändern. Im Wesentlichen müssen Sie den Code so schreiben write, als ob es versehentlich ein paar Zahlen überspringen kann und trotzdem funktioniert
Hier ist ein Beispiel: Du hast einen Float-Timer, was seinen Wert mit jedem Frame verringert, und sobald es null erreicht, etwas passiert.
Hier, wie man es nicht wert ist:
Timer–;
wenn(Timer == 0) Sachen machen();
Das funktioniert großartig im lokalen Spiel; aber online kann jeder Frame nur eine begrenzte Datenmenge senden, und höchstwahrscheinlich verringert sich der Timer-Wert alle drei oder mehr Frames. In diesem Fall, der Timer wird so verkürzt(vorausgesetzt, womit fängt es an 10):
10 -> 10 -> 10 -> 7 -> 7 -> 5 -> 5 -> 5 -> 2 -> 2 -> 2 -> -1 -> -1 -> -3……
Wie siehst du, 0 wurde übersprungen und doStuff() nie angefangen.
Hier, Wie man es repariert:
Timer–;
wenn(Timer <= 0) Sachen machen();
Leicht, oder? Und es wird wirklich funktionieren!
Aus diesem Grund gab es einen Fehler beim endlosen Laden der Internetkarte. London (Entwickler) verwendet ==, und wenn die erforderliche Anzahl aufgrund der Kurven der Karten übersprungen wurde, es ging weiter ins Unendliche. Zum Glück, es wurde in der Beta behoben.
Obwohl, Das ist nur die Hälfte des Puzzles, da es genauso wichtig ist, die richtigen Werte für die Synchronisation zu wählen. Zusätzlich zu den _ladenden und_gefeuerten Feldern, HugeLaser – das ist ein tolles beispiel für, wie Timer aussehen sollten.
Kurz, dann hier, was passiert da: 2 StateBinding für Animationen, Verwenden von Animationsindex und Sprite-Frame. Dies kann verwendet werden, um Animationen zu synchronisieren.
StateFlagBinding ist sehr nützlich, wenn Sie es mit mehreren booleschen Werten zu tun haben, z.B, wenn etwas offen ist, geschlossen oder gesperrt.
Es ist nur eine schnellere Möglichkeit, mehrere logische Zustandsbindungen durchzuführen, was eigentlich ziemlich veraltet ist.
Parameter müssen bool sein, um verwendet zu werden, und dann werden ihre Werte zu einer ushort komprimiert. Die Verwendung von ushort bedeutet, dass du nicht mehr haben kannst 16 logische Felder.
public StateBinding _laserStateBinding = (StateBinding) new StateFlagBinding(new string[3]
{
"_charging",
"_fired",
"doBlast"
});
public bool doBlast;
public bool _fired;
public bool _charging;
Beispiel aus HugeLaser. Tatsächlich, Felder _charging und_fired werden darin nicht verwendet, und es hängt vom Sprite-Frame und dem Animationsindex ab, welche, wiederum, synchronisiert. Und so rufst du am besten an, andernfalls würde es zu einer Verzögerung zwischen allen Clients kommen. Außerdem, in Writing Proper Code funktioniert dies mit StateBindigs (Teile).
Arten von StateBindigs, was du nicht wissen musst, da sie nur zur Optimierung verwendet werden:
Datenbindung(Zeichenfolgenfeld) – Arbeitet effizienter mit BitBuffer, und nur mit ihnen.
CompressedVec2Binding – Reduziert die Vec2-Größe. Alle Konstruktoren haben Maximalwerte.
CompressedFloatBinding – Verringert die Schwimmergröße. Alle Konstruktoren haben Maximalwerte.
InterpolatedVec2Binding[/b] – CompressedFloatBinding-Erweiterung mit höchster Priorität.
[ha]So schreiben Sie den richtigen Code, was mit StateBindnds funktioniert.[/ha]
Dies ist besonders wichtig, wenn Sie mit Dingen wie Timern oder Werten zu tun haben, die sich schnell ändern. Im Grunde musst du deine Sachen so schreiben, als könnten sie zufällig eine Reihe von Zahlen überspringen und am Ende immer noch funktionieren work.
Das ist besonders wichtig, wenn Sie es mit Timern oder Werten zu tun haben, die sich schnell ändern. Im Wesentlichen müssen Sie den Code so schreiben write, als ob es versehentlich ein paar Zahlen überspringen kann und trotzdem funktioniert
Hier ist ein Beispiel: Du hast einen Float-Timer, was seinen Wert mit jedem Frame verringert, und sobald es null erreicht, etwas passiert.
Hier, wie man es nicht wert ist:
Timer–;
wenn(Timer == 0) Sachen machen();
Das funktioniert großartig im lokalen Spiel; aber online kann jeder Frame nur eine begrenzte Datenmenge senden, und höchstwahrscheinlich verringert sich der Timer-Wert alle drei oder mehr Frames. In diesem Fall, der Timer wird so verkürzt(vorausgesetzt, womit fängt es an 10):
10 -> 10 -> 10 -> 7 -> 7 -> 5 -> 5 -> 5 -> 2 -> 2 -> 2 -> -1 -> -1 -> -3……
Wie siehst du, 0 wurde übersprungen und doStuff() nie angefangen.
Hier, Wie man es repariert:
Timer–;
wenn(Timer <= 0) Sachen machen();
Aus diesem Grund gab es einen Fehler beim endlosen Laden der Internetkarte. London (Entwickler) verwendet ==, und wenn die erforderliche Anzahl aufgrund der Kurven der Karten übersprungen wurde, es ging weiter ins Unendliche. Zum Glück, Dies wurde in der Beta behoben, obwohl, Das ist nur die Hälfte des Puzzles, da es genauso wichtig ist, die richtigen Werte für die Synchronisation zu wählen. Zusätzlich zu den _ladenden und_gefeuerten Feldern, HugeLaser – das ist ein tolles beispiel für, wie Timer aussehen sollten., dann hier, was passiert da: 2 StateBinding für Animationen, Verwenden von Animationsindex und Sprite-Frame. Damit können Animationen synchronisiert werden und es gibt auch ein bool doBlast-Feld., was wahr wird, als der Besitzer die Waffe abgefeuert hat. UND, wenn doBlast wahr ist (weil es eine StateBinding ist) und du bist nicht der besitzer, es gibt einen Schuss.
Es folgen die wichtigsten Teile., die keinen Doppelschuss und sofortiges Herunterfahren von doBlast erlauben, damit andere Spieler immer aktualisierte Spiele erhalten
Eigenschaften der NetWork-Klasse und mehr
– Ist der aktuelle Computer ein Host?.
– Ist der aktuelle Computer ein Host?.
– ist das Netzwerk aktiv.
Beim Erstellen von AmmoType, geeignet für Multiplayer, es muss einen leeren Konstruktor haben (keine Parameter).
Er ist es, der gerufen wird, so, wenn Sie einen anderen Konstruktor haben, es funktioniert überhaupt nicht und stürzt beim Entpacken der Nachricht ab.
dieses.Feuer() online synchronisiert.
Tatsächlich, synchronisierter Kugel-Spawn, aber wenn deine Waffe mit gewöhnlichen Gegenständen schießt, Sie müssen keine zusätzlichen Maßnahmen ergreifen, Sichergehen, dass alles synchron ist.
OnHoldAction wird synchronisiert, und OnReleaseAction nicht.
Das heisst, dass Sie OnHoldAction verwenden können, um Dinge aufzuladen (wie geht das mit Phaser). Aber du wirst einen Weg brauchen, um sicher zu gehen, dass OnReleaseAction synchronisiert wird (z.B, mit this.Fire, wie in Phaser, oder mit StateBinding).
Verbindungen und wie man Dinge hervorbringt
Für solche Sachen, als Holdables, es gibt optimierung – sobald sie abgeholt werden, sie ändern die Verbindung zu Enten. Aber wenn du, z.B, etwas Neues erschaffen, was macht was, Sie müssen ein paar zusätzliche Schritte ausführen., Was musst du tun, um die Kiste zu spawnen, wenn dein Mod für lokales Spielen ist:
Level.Hinzufügen(neue Kiste(500,500));
if(isServerForObject)
Level.Add(new Crate(500,500));
Ja, du brauchst eine zusätzliche Bedingung, und deshalb:
Da jeder Client seinen eigenen Waffencode läuft, аспавнится 2 Kisten; eine lokal, und der zweite vom Eigentümer des Objekts.
isServerForObject-Prüfungen, ob der aktuelle Computer das Objekt besitzt, damit das Objekt nur einmal spawnt.
Tatsächlich, Du kannst nachschauen, ist die Ente lokal, mit isServerForObject.
Tatsächlich, da ist noch was, worüber du Bescheid wissen solltest, und das ist Schmusen. Tatsächlich, es ermöglicht Ihnen, die Kontrolle über das Ding zu übernehmen und eine Verbindung herzustellen.
public void Fondle(Thing t)
{
if (t == null || !Network.isActive || (!this.isServerForObject || !t.CanBeControlled()) || (this.connection != DuckNetwork.localConnection || t.connection == this.connection))
return;
t.connection = this.connection;
++t.authority;
}
Dies wird verwendet, um die Dinge synchron zu halten, die du nicht aufnimmst, wie, z.B, Ferngesteuertes Auto, Um zu steuern, wählen Sie nur den Controller. Merken, dass Fondle existiert. Er wird dein Leben retten, wenn du versuchst etwas Kompliziertes zu machen und es online nicht funktioniert.
Wenn Sie ein Beispiel sehen möchten, Sehen Sie sich den Code der RCController-Klasse und deren Update an. Dies wird auch in aitemboxes verwendet..
Bitpuffer’ы
BitBuffer enthalten Daten, die du an andere senden kannst. Wenn Sie komplexere NetMessage ausführen möchten, es ist wichtig zu wissen, wie arbeiten Sie.
Tatsächlich, BitBuffer enthalten Daten und ermöglichen es Ihnen, sie als verschiedene Typen zu lesen / zu schreiben. BitBuffer hat eine Position, bedeutet, dass, wo liest du gerade daten aus. Und wenn die Werte gelesen werden, erhöht sich die Position. Auf diese Weise, Sie müssen sich nicht mit Variablen auseinandersetzen, aber nur mit der reihenfolge, wie sie platziert wurden.
BitBuffer myData = neuer BitBuffer();
myData.Write((Byte)25);
myData.Write((int)50);
myData.Write(Stimmt);
myData.Write(neue Vec2(25,25));
BitBuffer sameData = neuer BitBuffer(myData.buffer);
byte mybyte = sameData.ReadByte();
int myInt = sameData.ReadInt();
bool myBool = sameData.ReadBool();
Vec2 myVec = sameData.ReadVec2();
Sehr wichtig, damit die Reihenfolge von Lesen und Schreiben gleich ist, sonst geht es nicht.
Hier ist ein Beispiel, Wie kann ich ein Array in BitBuffer schreiben?, bei Gelegenheit, wenn es dir hilft es zu verstehen Wenig besser.
int[] myArray = new int[] { 5,125,123123,67,2,324 };
BitBuffer myData = neuer BitBuffer();myData.Write(myArray.Länge); //Array-Größe
für jedes (var num in myArray)
myData.Write(Auf eins); //schreibe jede ZahlBitBuffer sameData = new BitBuffer(myData.buffer);int arraySize = sameData.ReadInt(); //das erste int lesen (Größe)
int[] theArray = new int[ArrayGröße];zum (int ich = 0; ich < ArrayGröße; ich++)
dasArray = sameData.ReadInt(); //Lesen Sie jede Zahl und fügen Sie sie in ein Array ein[/Zitat]BitBuffer-Limit.Jeder BitBuffer speichert seine Größe in ushost, das ist eine positive Zahl von 0 zu 2?? oder 65535. Und da jedes Byte besteht aus 8 Seite, BitBuffer kann bis zu 8kb Daten enthalten.
Benutzerdefinierte Netzwerknachrichten
Synchronisiere Dinge mit StateBinding und sei sicher, dass sie die Rechteinhaber haben, reicht nicht immer aus, um richtig zu arbeiten.
DuckGame lädt alle NetMessages, bevor Mods geladen werden, damit Ihre NetMessage-Typen nicht zur Netmessage-Liste hinzugefügt werden.
Um dies zu bekämpfen, müssen wir unsere MessageTypes hinzufügen.
public static void UpdateNetmessageTypes()
public static void UpdateNetmessageTypes() {
IEnumerable subclasses = Editor.GetSubclasses(typeof(NetMessage));
Network.typeToMessageID.Clear();
ushort key = 1;
foreach (System.Type type in subclasses)
{
if (type.GetCustomAttributes(typeof(FixedNetworkID), false).Length != 0) {
FixedNetworkID customAttribute = (FixedNetworkID)type.GetCustomAttributes(typeof(FixedNetworkID), false)[0];
if (customAttribute != null)
Network.typeToMessageID.Add(type, customAttribute.FixedID);
}
}
foreach (System.Type type in subclasses) {
if (!Network.typeToMessageID.ContainsValue(type)) {
while (Network.typeToMessageID.ContainsKey(key))
++key;
Network.typeToMessageID.Add(type, key);
++key;
}
}
}
public override void OnPostInitialize() {
UpdateNetmessageTypes();
}
public override void OnPostInitialize() {
UpdateNetmessageTypes();
}
Voila, jetzt kannst du NetMessage in deinem Mod verwenden.
Tatsächlich, alle NetMessage, die du verwenden wirst, das sind veranstaltungen (Veranstaltungen).
Sie können auch normale NetMessage verwenden(NMDuckNetzwerk), aber nur, wenn Sie Daten an andere senden, wie benutzerdefinierte Hüte.
Ihre hypothetische NetMessage funktioniert in etwa so:
OnSerilize override füllt den serializedData-Bitpuffer mit Daten aus jedem Feld in Ihrer Klasse (im Gegensatz zu StateBidning, Eigenschaften funktionieren nicht), dann werden sie von interwebs weitergeleitet(Interwebs), wo sie nach Erhalt die OnDeserilize-Überschreibung durchlaufen, wo es alle Werte aus dem Bitpuffer in die richtigen Felder einfügt.
Für alle Netmessages benötigen Sie einen leeren Konstruktor, weil, wenn du die nachricht bekommst, ein neues Objekt dieses Typs wird erstellt, einen leeren Konstruktor verwenden. Wenn du es nicht hast, du wirst zerquetscht (kein Anime-Spieler).
Hier, wie eine einfache NetMessage aussieht:
class NMTimeLimit : NMDuckNetworkEvent
{
public string message;
public NMTimeLimit()
{
}
public NMTimeLimit(string msg)
{
message = msg;
}
public override void Activate()
{
base.Activate();
HUD.AddInputChangeDisplay(message);
}
}
Hier ist eine Liste aller Datentypen, die automatisch serialisiert und deserialisiert werden(sehr wichtig, die Felder öffentlich machen! andernfalls werden sie nicht serialisiert):
- Schnur
- schweben
- doppelt
- Byte
- sbyte
- bool
- kurz
- ukurz
- int
- uint
- lange
- ulong
- verkohlen
- Vec2
- Alles, was erbt oder ist Ding
Bei der Anwendung auf Enten: Byte für netIndex verwenden.
duck.profile.networkIndex
und dann eine Ente finden, mit
DuckNetwork.profiles[(int) Index].Ente;
Auf diese Weise, alle deine Nachrichten, Entenbezogen, wird viel schneller versenden, und die Wahrscheinlichkeit von Synchronisationsfehlern wird stark reduziert.
Und was, Wenn du eine Art Array willst? Da es nicht automatisch unterstützt wird, wir müssen die Überschreibungen von OnSerilize und OnDeserilize ändern.
int[] myArray = new int[] { 25,30,29};
protected override void OnSerialize()
{
base.OnSerialize(); //это то, что автоматически сериализирует поля, так что не убирайте это.
serializedData.Write(myArray.Length);
foreach (var num in myArray)
serializedData.Write(num); //то же самое, что и в примере с BitBuffer'ом
public override void OnDeserialize(BitBuffer msg)
{
base.OnDeserialize(msg); //автоматически десериализирует поля, не убирайте это.
//так как при сериализации base.OnSerilize выполнялся первым, base.OnDeserilize тоже должен выполняться первым.
myArray = new int[msg.ReadInt()];
for (int i = 0; i < myArray.Length; i++)
myArray[] = msg.ReadInt();
}
//по сути, вам нужно выяснить, как превратить что-либо в BitBuffer
Da alles in duckgame mit BitBuffer gesendet wird, die maximale Größe einer Netmessage-Nachricht beträgt 8 KB minus Headergröße (Header (Überschrift) enthält Informationen zu NetMessage, damit es wieder in einen Byte-Heap-NetMessage umgewandelt werden kann).
Weg, womit DuckGame dieses Problem löst, ist die Verwendung von NMLevelDataHeader, mehrere NMLevelDataChunks, das sind Fragmente des Levels, das lässt sich zusammenstellen, und nach Erhalt wird eine NMLevelDataReady-Nachricht gesendet, bestätigen, dass die Übertragung abgeschlossen ist.
Auch wenn es funktioniert, Ich würde nicht empfehlen, den Code zu kopieren, da es nur funktioniert, wenn eine Übertragung gleichzeitig mit einem Client oder allen Clients gleichzeitig durchgeführt wird. Ich habe einen Beitrag verwendet, enthält Sitzung und beendet bool (Sitzung und ein fertiger bool), und dann hatte ich einen datenmanager, wer hat es rausgefunden, wenn eine neue Sitzung eröffnet wurde, und als es zu Ende war, mit dem letzten bool. Vor, wie DuckGame seine NetMessages sendet, es sortiert sie nach Größe, und daher alle Nachrichten, außer dem letzten, muss gleich groß sein, und letzteres sollte weniger sein, wenn Sie sich entscheiden, die empfangenen Daten beim Empfangen einfach in ein Array einzufügen.
Anstatt das Array zu sortieren, als ich es gesendet habe, Ich habe beschlossen, es früher zu sortieren, wenn alle Daten empfangen wurden. Jetzt, wenn ich daran denke, Ich bin mir nicht sicher, welcher weg war besser, aber das ist egal. Auf Jedenfall, hier ist der code, zeigen, Wie kann ich das machen (aus der Mod des Autors, Reskins), mit dem Transfermanager-Ansatz: DataTransferSession[github.com], DataTransferSession[github.com] und NMDataSlice[github.com]
Da ich ein guter Programmierer bin und flexiblen Code geschrieben habe, Sie können Dateien kopieren und die DataTransferManager.SendLotsOfData-Funktion verwenden und etwas mit dem DataTransferManager.onMessageCompleted-Ereignis verbinden, und alles sollte ok sein.
NetDebug
Um es zu aktivieren, Rechtsklick auf das Spiel in der Bibliothek:
Geben Sie im sich öffnenden Fenster den Startparameter in das entsprechende Feld ein:
Um es zu aktivieren, Rechtsklick auf das Spiel in der Bibliothek:
Kommentar hinterlassen