26.06.2026 21:32

Moonlight Animate: dołączanie do sesji współpracy bez pustego płótna (late-join v2)

⏱️ 4 min czytania
Udostępnij:

Współpraca w czasie rzeczywistym ma jeden nieoczywisty, trudny moment: co widzi osoba, która dołącza w środku sesji? Wcześniejsza wersja radziła sobie z tym dobrze dla scen animacji — nowy uczestnik dostawał zapisaną scenę i „dogrywkę" wszystkich operacji z dziennika serwera, więc widział aktualny stan. Ale dla sesji na płótnie rastrowym (malowanie) była dziura: serwer nie przechowywał żadnego zrzutu pikseli, więc późny uczestnik startował od… pustego obrazu. Domykamy to w tej fazie.

Problem

Podczas sesji współpracy operacje (pociągnięcia pędzla, zmiany struktury warstw, geometria dokumentu) są sekwencjonowane i journalowane na serwerze — to pozwala odtworzyć je u kogoś, kto dołączył później. Ale odtwarzanie wszystkiego od zera ma dwie wady:

  1. Płótno rastrowe nie miało punktu startowego. Bez bazowego obrazu odtwarzanie samych „różnic" nie miało na czym usiąść — joiner widział pustkę.
  2. Długie sesje = wolne dołączanie. Im dłużej trwa sesja, tym dłuższy dziennik do odtworzenia.

Rozwiązanie: baza od gospodarza + kompakcja

W sesji rastrowej gospodarz (właściciel sesji) co kilkadziesiąt sekund utrwala na serwer pełną bazę dokumentu — kompletny zrzut projektu w natywnym formacie. Wraz z nią serwer zapamiętuje znacznik mówiący, do którego momentu historii ta baza sięga.

Dzięki temu osoba dołączająca:

  • pobiera bazę i ustawia nią aktualne piksele (zamiast pustki),
  • a potem dogrywa tylko ogon operacji — te, które pojawiły się po utrwaleniu bazy.

Po utrwaleniu bazy serwer kompaktuje dziennik (usuwa operacje już zawarte w bazie), więc kolejni uczestnicy dołączają się szybko nawet w bardzo długich sesjach.

Najtrudniejsze: dokładność do jednej operacji

Cała poprawność stoi na jednym warunku: baza musi odzwierciedlać dokładnie te operacje, których znacznik dotyczy — ani jedną mniej, ani jedną więcej. Inaczej dołączający albo zgubiłby zmianę, albo zastosowałby ją podwójnie (np. warstwa dodana dwa razy).

Rozwiązaliśmy to kilkoma sprzężonymi mechanizmami:

  • Gospodarz utrwala bazę tylko, gdy nie ma własnej operacji „w locie". Wtedy odczyt znacznika i serializacja dokumentu odbywają się w jednym, nieprzerywalnym kroku, więc baza odpowiada dokładnie znanemu numerowi.
  • Baza jest wersjonowana numerem operacji, a serwer zawsze wydaje tę wersję, która pasuje do zatwierdzonego znacznika — i dokłada ten numer w nagłówku odpowiedzi.
  • Dołączający przykleja swój punkt startowy odtwarzania do numeru z tego nagłówka, a nie do wartości sprzed pobrania. Dzięki temu „baza" i „od którego miejsca dogrywać" to zawsze ten sam numer — nawet jeśli gospodarz zaktualizował bazę dokładnie w trakcie czyjegoś dołączania.
  • Okno bezpieczeństwa przy kompakcji: najświeższe operacje zostają w dzienniku jeszcze przez chwilę, żeby ktoś dołączający w trakcie nic nie zgubił.

Te scenariusze wyłapał wewnętrzny adwersaryjny przegląd — race przy równoległym zaawansowaniu bazy potrafił podwoić operacje, dlatego trafił na listę napraw przed wydaniem.

Przy okazji: kafelkowanie w Texture Ripperze

Domknęliśmy też drobiazg w narzędziu Texture Ripper (wyrywanie tekstur ze zdjęć z korekcją perspektywy). Przełącznik „kafelkowalna" na pojedynczym ripperze respektuje teraz wybraną w panelu metodę leczenia szwu (Content-Aware, Blend, Mirror, Scattered) i szerokość pasa szwu — wcześniej przełącznik ignorował te ustawienia i zawsze używał jednego trybu. Wybór zapisuje się w pliku projektu i synchronizuje podczas współpracy.

Co to daje

  • Dołączenie do trwającej sesji malowania pokazuje aktualny obraz, nie pustkę.
  • Długie sesje dołączają się szybko, bo nie trzeba odtwarzać całej historii.
  • Kafelkowalne tekstury z Texture Rippera robią dokładnie to, co ustawisz w panelu.

Sesje scen animacji działają jak dotąd — ta zmiana dotyczy płótna rastrowego i porządkuje fundament pod jeszcze płynniejszą współpracę.

📚 Powiązane posty

14.07.2026 22:19

Silnik gier: od szablonu do grywalnej gry RPG top-down

Startowy szablon silnika gier to teraz kompletny, grywalny wycinek gry RPG top-down: chodzenie po siatce, rozmowy z postaciami, przeciwnik goniący gdy...
Czytaj dalej →
14.07.2026 19:16

Moonlight Animate — system dialogów w grach

Silnik gier 2D w Moonlight Animate ma teraz kompletny system dialogów: kwestie z efektem maszyny do pisania, wybory z warunkami, zmienne fabularne i g...
Czytaj dalej →
14.07.2026 18:40

Moonlight Animate: ruchoma panorama dźwięku także w wyeksportowanym wideo

Dźwięk przejeżdżający z lewego do prawego głośnika brzmiał dobrze w podglądzie, ale eksport przez serwer zamrażał go w miejscu. Ostatnia luka parytetu...
Czytaj dalej →

📧 Bądź na bieżąco!

Zapisz się na listę oczekujących, aby otrzymywać powiadomienia o nowych postach na Dev Blog oraz informacje o starcie platformy Moonlight.