Angreifen, Verteidigen und Raiden

Vorschläge für die 5. Runde Last-War
Antworten
Benutzeravatar
firebat
weiß was er tut
weiß was er tut
Beiträge: 301
Registriert: 31. Jan 2019, 16:44

Angreifen, Verteidigen und Raiden

Beitrag von firebat »

Hallo liebe LW-Gemeinschaft,

wir haben in der letzten Runde gemerkt, dass beim Management der Flotten und auch beim Raiden vieles noch nicht rund läuft. Da die Probleme eigentlich gut im Bugforum dokumentiert sind, möchte ich hier nur eine stichpunktartige Zusammenfassung schreiben.

Verteidigungsflotten werden manchmal nicht berücksichtigt. Obwohl sie rechtzeitig an Ort und Stelle sind, tauchen sie nicht in Kampfberichten auf.
Dagegen tauchen ankommende Flotten gerne in Kampfberichten verfrüht auf - unabhängig davon, ob sie einfach auf einem Rückflug oder auf Verteidigungsmission waren. Die Kombination dieser beiden Fehler lassen einen manchmal überlegen, ob es nicht sinnvoller wäre eine Verteidigungsmission auf nach dem KB zu timen - so lange der Kampf oder die Flottenankunft durch niemanden zeitnah getriggert wird, könnte verspätetes Ankommen fast günstiger sein. Zusätzlich wurden auch Fehlinformation von Observern berichtet, die gerne mal Verteidigungsflotten nicht angezeigt haben, obwohl diese bereits an Ort und Stelle waren und auch am Kampf teilgenahmen.

Bei Raidenflotten gab es große Probleme mit den Ausladen von Ressorcen. Viele Spieler berichteten von ganzen Wellen die keine Rohstoffe ablieferten. Besonders oft wurde das Problem berichtet, als die Flotten in Abwesenheit vom Besitzer zurückkamen. Weiter geht es bei den Doppel-KBs bei denen zwei Flotten drei Kampfberichte erzeugten. Der dritte Kampfbericht verdoppelt den ersten Kampfbericht, allerdings datiert er auf die Heimkehrzeit der ersten Flotte - daher taucht er auch gerne als zukünftiger Kampfbericht in der Inbox auf. In dem DoppelKB werden grundsätzlich keine Rohstoffe mitgenommen (alle Rohstoffwerte sind 0), was zum Überschreiben und damit zum Verlust aller geraideten Rohstoffe des ersten Kampfes führt. Diese DoppelKBs tauchten gerne auf, wenn die zwei Flotten als Welle mit einem Zeitabstand von nur wenigen Sekunden oder Minuten auf einem Planeten ankamen. Allerdings wurde auch von DoppelKBs berichtet, bei denen die beiden Flotten zwar denselben Account aber verschiedenen Planeten anflogen und dabei einen zeitlichen Abstand von vielen Minuten oder auch -sehr selten- über eine Stunde auseinander lagen.

Bei großen Kampfberichten mit vielen Schiffen traten auch Doppelschläge auf. Zwei KBs wurden generiert, die nur Sekunden auseinander lagen. Die angreifende Flotte blieb identisch, die Verteidigung wurde 2. getroffen. Das trat prominent in Runde3 auf. Von runde 4 kenne ich aktuell allerdings kein konkretes Beispiel.

Ich schreibe diese Zusammenfassung, weil das Spiel maßgeblich auf Angriffen und dem Raiden von Ressourcen aufbaut - das sind zentrale Punkte von Last-War. Viele Spieler stecken viel Energie in die Flottenverwaltung. Nicht nur Fleeter überwachen ihre Ziele genau, um Verluste zu vermeiden, sondern auch der normale Sicherungsflug der Hausflotte überflüssig gemacht, wenn die Flotte in einem KB zwei Stunden verfrüht auftaucht. Durch die beschriebenen Fehler wird das alles ins Absurde gezogen und das Spiel in einer seiner Hauptaufgaben unzuverlässig. Die Fehler sind leider schon seit mindestens Runde 3 bekannt und haben sich in Runde 4 erneut bestätigt.

Wir sind uns alle drüber klar, dass die Eventverwaltung nicht trivial ist. Trotzdem sollten diese Fehler nicht einfach akzeptiert werden, nur weil es in den meisten Fällen doch irgendwie gut klappt.

edit: Schreibfehler.

Beste Grüße
Zuletzt geändert von firebat am 4. Jun 2020, 19:41, insgesamt 2-mal geändert.

Benutzeravatar
Vanitas
weiß was er tut
weiß was er tut
Beiträge: 300
Registriert: 29. Jan 2019, 18:45
Allianz: [HdO] Herren der Ordnung

Re: Angreifen, Verteidigen und Raiden

Beitrag von Vanitas »

Sehr schöne Zusammenfassung der wohl größten Baustelle hier. Leider gab es zu vielen dieser Punkte nie eine detailierte Äusserung Seitens VR, weshalb leider oft der Eindruck entstand das diese Punkte so wie sie sind hingenommen werden.

Allerdings weiß wohl jeder der den Discord und auch das Forum genauer verfolgt das dies die Hauptgründe sind, weshalb viele Leute aufgehört haben bzw. keine neue Runde mehr starten werden. Bevor man also auch nur an andere Punkte ala Balancing oder neuen Feature nachdenkt, sollte man sich massiv mit diesen Themen auseinandersetzen. Erst wenn diese ganzen Events korrekt funktionieren werden sich die Leute wieder die Mühe machen Ihre Zeit hier zu investieren.

Wenn eine Runde 5 wieder so startet wie es bei Runde 4 der Fall war, sprich das die Fehler weiter existieren da sie als nicht wichtig genug eingestuft wurden, werden die Spielerzahl drastisch sinken. Jeder der dies nun zwei Runden mitgemacht hat wird es sich kein drittes mal mehr antun.
X-Wars History
Beta 2: HdO (HdO-GC)
Beta 3: HdO (|L|-GC, HdO-FA, BK-HC)
Beta 4: HdO (HdO-FA, HdO-HC, BK-HC)
Beta 5: Lof (Lof-HC, BK-HC) nach Auflösung zur HdO (HdO-FA, HdO-HC, BK-HC)
Beta 6: HdO (HdO-HC, |E|-HC) nach Auflösung zur TDM (TDM-Member)

Okki
Schreiberling
Schreiberling
Beiträge: 42
Registriert: 5. Feb 2019, 18:20

Re: Angreifen, Verteidigen und Raiden

Beitrag von Okki »

Die Probleme mit fehlenden Resourcen und doppelten Kampfberichten würde ich darauf zurückführen, dass mehrere Prozesse (Anfragen an den Webserver) gleichzeitig die gleichen Daten lesen und schreiben.

Beispiel: Anfrage A verarbeitet eine zu Planet X zurückkehrende Raidflotte und Anfrage B zur gleichen Zeit einen Handel an dem ebenfalls Planet X beteiligt ist:

A: Lesen der Lagerstände von Planet X aus der DB

A: Berechnen der neuen Lagerstände

B: Lesen der Lagerstände von Planet X aus der DB

B: Berechnen der neuen Lagerstände

A: Schreiben der neuen Lagerstände in die DB

B: Schreiben der neuen Lagerstände in die DB

=> Die Änderungen von A wurden von B überschrieben.

Solchen Race-Conditions begegnet man u.a. mit Transaktionen und Row-Level-Locks auf Datenbankebene. Bei MySQL kommt dafür nur die InnoDB Storage Engine in Frage, MyISAM kann nur komplette Tabellen sperren.

---

Für die Flotten-Probleme hab ich mir mal etwas Gedanken gemacht, wie man die Ereignisse möglichst einfach in der richtigen Reihenfolge abarbeiten kann.

Dazu benötigt man eine für die Flotten-Ereignisse mit folgenden Feldern:
- Zeitpunkt
- Planet
- Flotten-ID
- Ereignis-Typ
- Abhängigkeit von anderem Ereigniss

Alle Ereignisse werden beim Abschicken einer Flotte angelegt, auch die Rückkehr oder das Ende einer Verteidigungsmission. Wenn es für eine Flotte mehr als ein Ereignis gibt, wird als Abhängigkeit das vorherige Ereignis gesetzt.

Aktualisieren der Flottenereignissen:

Es gibt einen Stack, der die zu aktualisierenden Planeten samt Zeitpunkt, bis zu dem aktualisiert werden soll, enthält. Als Startwert wird der gewünschte Planet samt aktuellem Zeitstempel auf den Stack gelegt.
Dann wird solange der oberste Eintrag vom Stack gelesen (nicht entfernt) und das älteste Ereignis dieses Planeten bearbeitet, bis der Stack leer ist.
Wenn für den Planeten keine weiteren Ereignisse vorhanden sind, die älter als der Zeitstempel sind, wird der Planet vom Stack entfernt.
Wenn das zu bearbeitende Ereigniss eine Abhängigkeit enthält, wird der Planet und Zeitstempel dieser Abhängigkeit auf den Stack gelegt und die Bearbeitung des Ereignisses übersprungen.

Beim Bearbeiten eines Ereignisses wird dieses aus der Tabelle gelöscht und die darauf verweisende Abhängigkeit zurückgesetzt.

Hier mal ein kleines (schlecht kommentiertes) Beispiel:

Code: Alles auswählen

<?php

setup_database();
print("2x2x2:\n");
flottenereignisse_aktualisieren('2x2x2');

function flottenereignisse_aktualisieren($koordinaten) {
    $db = Database::getInstance()->handler;

    $zeitstempel = new Datetime("now"); 

    $planeten = [];
    array_push($planeten, ['koordinaten' => $koordinaten, 'zeitstempel' => $zeitstempel]);

    do {
        $planet = end($planeten);

        $db->beginTransaction();
        $query = $db->prepare('SELECT * FROM flottenereignisse WHERE koordinaten = ? AND zeitstempel <= ? ORDER BY zeitstempel ASC LIMIT 1 FOR UPDATE');
        $query->execute([$planet['koordinaten'], $planet['zeitstempel']->format('Y-m-d H:i:s')]);

        // Kein Ereignis für den aktuellen Planeten mehr ausstehend -> nächster Planet
        if( $query->rowCount() == 0 ) {
            $db->commit();

            array_pop($planeten);

            continue;
        }

        $ereignis = $query->fetch();

        // Ereignis hat eine Abhängigkeit -> zuerst Abhängigkeit auflösen
        if( $ereignis['abhängigkeit'] ) {
            $db->commit();

            $query = $db->prepare('SELECT * FROM flottenereignisse WHERE id = ?');
            $query->execute([$ereignis['abhängigkeit']]);
            $abhängigkeit = $query->fetch();
            array_push($planeten, ['koordinaten' => $abhängigkeit['koordinaten'], 'zeitstempel' => new Datetime($abhängigkeit['zeitstempel'])]);

            continue;
        }

        ereignis_verarbeiten($ereignis);

        $db->commit();
    } while (count($planeten) > 0);
}

function ereignis_verarbeiten($ereignis) {
    $db = Database::getInstance()->handler;

    $query = $db->prepare('DELETE FROM flottenereignisse WHERE id = ?');
    $query->execute([$ereignis['id']]);
    $query = $db->prepare('UPDATE flottenereignisse SET abhängigkeit = NULL WHERE abhängigkeit = ?');
    $query->execute([$ereignis['id']]);

    print("$ereignis[id]\t$ereignis[zeitstempel]\t$ereignis[koordinaten]\t$ereignis[flotten_id]\t$ereignis[typ]\n");
}

function setup_database() {
    $db = Database::getInstance()->handler;

    $sql = <<< EOD
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;


DROP TABLE IF EXISTS `flottenereignisse`;
CREATE TABLE `flottenereignisse` (
  `id` int(11) NOT NULL,
  `flotten_id` varchar(20) NOT NULL,
  `koordinaten` varchar(20) NOT NULL,
  `zeitstempel` datetime NOT NULL,
  `typ` varchar(20) NOT NULL,
  `abhängigkeit` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(1, 'aa', '3x3x3', '2020-06-07 01:30:00', 'überstellen', NULL);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(2, 'bb', '1x1x1', '2020-06-07 02:00:00', 'angriff', NULL);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(3, 'bb', '2x2x2', '2020-06-07 03:30:00', 'rückkehr', 2);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(4, 'cc', '3x3x3', '2020-06-07 02:30:00', 'angriff', NULL);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(5, 'cc', '2x2x2', '2020-06-07 04:00:00', 'rückkehr', 4);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(6, 'dd', '1x1x1', '2020-06-07 01:30:00', 'verteidigung_start', NULL);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(7, 'dd', '1x1x1', '2020-06-07 02:30:00', 'verteidigung_ende', 6);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(8, 'dd', '3x3x3', '2020-06-07 04:00:00', 'rückkehr', 7);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(9, 'ee', '2x2x2', '2020-06-07 02:00:00', 'transport', NULL);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(10, 'ee', '4x4x4', '2020-06-07 03:30:00', 'rückkehr', 9);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(11, 'ff', '5x5x5', '2020-06-07 02:15:00', 'angriff', NULL);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(12, 'ff', '4x4x4', '2020-06-07 03:45:00', 'rückkehr', 11);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(13, '00', '4x4x4', '2020-06-07 01:45:00', 'angriff', NULL);
INSERT INTO `flottenereignisse` (`id`, `flotten_id`, `koordinaten`, `zeitstempel`, `typ`, `abhängigkeit`) VALUES(14, '00', '5x5x5', '2020-06-07 03:15:00', 'rückkehr', 13);


ALTER TABLE `flottenereignisse`
  ADD PRIMARY KEY (`id`),
  ADD KEY `koordinaten` (`koordinaten`);


ALTER TABLE `flottenereignisse`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=15;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

EOD;

    $db->exec($sql);
}

abstract class Singleton
{
    private static $instances = array();
    protected function __construct() {}
    public static function getInstance()
    {
        $class = get_called_class();
        if (!isset(self::$instances[$class])) {
            self::$instances[$class] = new static();
        }
        return self::$instances[$class];
    }
    private function __clone() {}
    private function __wakeup() {}
} 

class Database extends Singleton
{
    public $handler;
    protected function __construct()
    {
        $this->handler = new PDO('mysql:host=localhost;dbname=lwdb', 'okki');
    }
}

?>
Flottenbewegungen:

Bild

Ausgabe:

Code: Alles auswählen

2x2x2:
9	2020-06-07 02:00:00	2x2x2	ee	transport
6	2020-06-07 01:30:00	1x1x1	dd	verteidigung_start
2	2020-06-07 02:00:00	1x1x1	bb	angriff
3	2020-06-07 03:30:00	2x2x2	bb	rückkehr
1	2020-06-07 01:30:00	3x3x3	aa	überstellen
4	2020-06-07 02:30:00	3x3x3	cc	angriff
5	2020-06-07 04:00:00	2x2x2	cc	rückkehr
Die Fertigstellung von Schiffe, Türmen, etc. muss da natürlich auch noch mit rein.

Antworten