26.07.2026 19:35

Moonlight Animate: trzecia tura po audycie — i trzy rzeczy, których nie naprawiliśmy

⏱️ 10 min read
Share:

To jest domknięcie historii, którą zaczęliśmy dwoma poprzednimi turami. Całościowy przegląd kodu Moonlight Animate zostawił po sobie długą listę: pierwsza tura zamknęła rzeczy najgorsze, druga — te, przez które praca mogła po cichu zniknąć. Została reszta. Około sześćdziesięciu pozycji, rozrzuconych po szesnastu obszarach: od silnika pędzla, przez kolor i eksport, po dźwięk i pracę zespołową.

Wzięliśmy całą tę listę naraz. Wyszło niecałe sto plików i cztery tysiące zmienionych linii.

Ale zacznę od czegoś, co jest w tej turze najciekawsze — i najbardziej niewygodne.

Trzy pozycje z listy okazały się nieprawdziwe

Audyt to też tylko czyjaś praca, a znajdowanie błędów w kodzie jest zajęciem, w którym łatwo o fałszywy alarm. Kiedy masz listę zarzutów i zadanie „napraw to", istnieje bardzo silna pokusa, żeby po prostu odhaczać. Trzy pozycje nie przeżyły sprawdzenia.

Najpoważniejszy zarzut brzmiał: cofnięcie operacji obejmującej cały dokument — na przykład scalenia warstw — kasuje też zmiany zrobione po niej. To by było paskudne. Prześledziliśmy każdą ścieżkę, którą zmiana może trafić do dokumentu, łącznie z tymi przychodzącymi od innych uczestników sesji zespołowej. Wszystkie trafiają na stos historii. Skoro tak, to żeby w ogóle dojść do tamtej migawki, trzeba było wcześniej cofnąć wszystko, co po niej. Zarzut się nie broni.

Ale tuż obok znaleźliśmy coś prawdziwego, i to tej samej rodziny. Migawka dokumentu zapisuje źródło stempla klonującego i domyślny styl tekstu — a żadna operacja ich nie posiada. To zwykłe ustawienia, poza historią. Efekt: ustawiasz źródło stempla, grupujesz warstwy, ustawiasz nowe źródło, naciskasz Ctrl+Z — i źródło po cichu wraca do starego. W historii nie ma o tym ani słowa. Drobiazg, ale z gatunku tych, które każą podejrzewać, że program ma własne zdanie.

Drugi obalony zarzut dotyczył sesji zespołowych i wskazywał pole, które w kodzie nie istnieje. Sedno było jednak trafne: okno z prośbą o przejęcie warstwy nie reagowało na to, że proszący w międzyczasie wyszedł. Po minucie warstwa trafiała do kogoś, kogo już nie ma. To naprawiliśmy.

Trzeci: „nagrywanie akcji rejestruje tylko operacje ze skryptów". Nieprawda — filtry i korekty zapisują się niezależnie od tego, czy uruchomisz je z menu, czy ze skryptu. Prawdziwa dziura jest węższa: praca narzędziami na płótnie rzeczywiście nie zostawia śladu. Zamiast udawać, że to naprawiliśmy, zrobiliśmy dwie uczciwsze rzeczy. Opis w kodzie mówi teraz dokładnie, co się nagrywa, a co nie. A samo nagrywanie pilnuje, czy liczba zapisanych kroków zgadza się z liczbą zmian w dokumencie — jeśli nie, dopisuje do nagrania ostrzeżenie. Lepiej, żeby nagranie powiedziało „czegoś tu brakuje", niż żeby odtworzyło się po cichu inaczej.

Ołówek, który nie rysował co piątej kropki

Ta jest moja ulubiona, bo to czysta geometria.

Ołówek o grubości jednego piksela ma promień pół piksela. Program stawiał kropkę tam, gdzie środek pędzla trafiał w środek piksela. Problem w tym, że najdalszy punkt kwadratu od najbliższego środka piksela leży w rogu, w odległości około 0,707 — czyli więcej niż promień. Pędzel prowadzony blisko rogów pikseli nie trafiał w nic. Około jednej na pięć kropek nie malowała się w ogóle, a idealnie pozioma albo pionowa kreska poprowadzona po całkowitej współrzędnej nie pojawiała się wcale, bo wszystkie cztery środki leżały dokładnie za daleko.

Podpowiedź z audytu brzmiała: poszerzyć próg o pół piksela. Nie zrobiliśmy tego — poszerzyłoby to każdy pędzel w programie o pół piksela z każdej strony, a przy najmniejszym rozmiarze zamieniało kropkę w kwadrat dwa na dwa. Zamiast tego przyjęliśmy zasadę, którą i tak stosuje tryb jednopikselowy: piksel, w którym leży środek kropki, jest zawsze pomalowany. Pędzle od półtora piksela w górę rysują się dokładnie tak samo jak wcześniej.

Ustawienie, które nic nie robiło

W panelu koloru jest przełącznik silnika mieszania farb. Można było wybrać drugi silnik. Wybór zapisywał się, interfejs go pokazywał — a pędzel na płótnie konsekwentnie używał pierwszego. Kontrolka bez żadnego połączenia z tym, na co miała wpływać.

Podobnie „ciemniejszy rdzeń" ołówka: w domyślnych ustawieniach dawał obraz co do bajtu identyczny z wyłączonym. Podnosił górny limit krycia, który bez włączonej reakcji na nacisk i tak wynosi sto procent. Opis pod spodem odsyłał użytkownika do ustawienia, które w ogóle nie zasila tego mechanizmu.

Takie rzeczy są gorsze od zwykłych błędów. Błąd widać. Kontrolka-atrapa uczy, że nie warto dotykać ustawień, bo i tak nic z tego nie wynika.

Komunikat, który kłamał w stronę drukarni

Przy eksporcie z profilem kolorystycznym program wyświetlał: „zapisano z osadzonym profilem". Także wtedy, gdy profil był w przestrzeni CMYK — a takiego formatu obrazu, w którym zapisywaliśmy, z definicji nie da się takim profilem opisać. Plik wychodził oznaczony zupełnie inaczej, niż mówił komunikat.

To nie jest kosmetyka. Ktoś z takim plikiem idzie do druku, w zaufaniu do zdania na ekranie. Teraz komunikat mówi, co naprawdę trafiło do pliku, i wskazuje format, w którym profil CMYK faktycznie przetrwa.

Z tej samej rodziny: eksport 16-bitowy dawał inne kolory niż 8-bitowy z tego samego dokumentu. Pomijał przeliczenie z przestrzeni roboczej i nie dołączał profilu. Przy okazji wyszło drugie, mniejsze źródło rozjazdu — podkładanie bieli pod przezroczystość liczone było w złej dziedzinie, przez co półprzezroczyste krawędzie wychodziły odrobinę inaczej.

Uchwyty, których nie dało się złapać

Uchwyty kadrowania i przekształcania miały strefę trafienia liczoną w pikselach obrazu, nie ekranu. Przy oddaleniu do jednej czwartej strefa robiła się ćwierć wielkości i złapanie narożnika graniczyło z cudem. Przy dużym zbliżeniu odwrotnie — strefy puchły tak, że zjadały tryb przesuwania.

Poprawiliśmy jedno i drugie: i obszar kliknięcia, i sam rysunek uchwytów. To ważne, żeby zrobić naraz — bo poprawienie samej strefy trafienia przy pozostawieniu rysunku dałoby stan gorszy niż przed naprawą: kwadracik większy niż miejsce, w które trzeba kliknąć.

Rzeczy, które psuły się po cichu

Kilka pozycji łączy jeden wzorzec: program wiedział, że coś poszło źle, i nie mówił.

Nieudany eksport filmu meldował sukces. Zapełniony dysk kończył się przyciętym, niegrywalnym plikiem i komunikatem, że zapisano. Kody błędów z zapisu były odczytywane i pomijane — we wszystkich trzech miejscach, w których eksportujemy wideo.

Dźwięk pulpitu grał o niecałe dziewięć procent za szybko. System oddaje dźwięk w swojej własnej częstotliwości próbkowania, a my tę informację ignorowaliśmy, zakładając jedną wartość na sztywno.

Dryf dźwięku przy nagrywaniu. Gdy bufor się przepełniał, porcja dźwięku była po cichu porzucana — ale znacznik czasu przesuwał się tylko o to, co faktycznie zapisano. Każde takie zdarzenie trwale przesuwało ścieżkę względem obrazu.

Skrypt w nieskończonej pętli zawieszał program na stałe. Także wtyczka — a te ładują się przy starcie, więc jedna felerna potrafiła zablokować uruchomienie. Rozwiązanie: limit liczy bezczynność, nie czas pracy. Skrypt eksportujący tysiąc plików przez godzinę działa bez przeszkód, bo cały czas coś robi. Pętla, która nie robi nic, jest przerywana.

Klatka kluczowa bez drogi powrotnej

W inspektorze węzłów przycisk z rombem dodaje klatkę kluczową parametru. Tylko dodaje. Model danych miał funkcję usuwania od zawsze — po prostu nic w całym programie jej nie wywoływało. Jedno przypadkowe kliknięcie animowało parametr na trwałe. Teraz przycisk jest przełącznikiem.

Sesje zespołowe: dziura, przez którą można było się podszyć

Bezpośredni kanał między uczestnikami przyjmował dowolny rodzaj wiadomości, a tożsamość nadawcy brał z treści tej wiadomości. Lista dozwolonych operacji była sprawdzana tylko przy wysyłaniu. Ktoś mógł więc podać się za innego uczestnika i przepchnąć zmiany z pominięciem normalnej drogi. Teraz tożsamość pochodzi z samego połączenia, a wiadomość, która twierdzi co innego, jest odrzucana — nie „poprawiana", bo cicha korekta ukryłaby fakt, że coś jest nie tak.

Co zostało nienaprawione — i dlaczego to piszemy

Sześć rzeczy z tej listy świadomie zostawiliśmy, każdą z powodem zapisanym obok.

Pełna struktura grup przy eksporcie do formatu warstwowego wymaga rozbudowy biblioteki, z której korzystamy — zrobiliśmy część możliwą dziś: zawartość ukrytej grupy nie trafia już do pliku jako widoczna. Podgląd na karcie graficznej nadal nie zna jednego z trybów mieszania kolorów dla druku, więc w tym konkretnym ustawieniu podgląd różni się od pliku. Automatyczny zapis sceny działa teraz na głównym wątku — wyścig z edycją zniknął całkowicie, kosztem krótkiej pauzy przy dużych projektach; właściwe rozwiązanie wymaga zmiany, która nie mieści się w tej turze.

Piszemy to, bo lista „naprawione" bez listy „nie naprawione" jest niepełna w sposób, który z czasem robi się kosztowny. Rzecz odłożona z powodem wraca. Rzecz odłożona po cichu — zostaje.

Skąd wiemy, że to działa

Silnik pędzla dostał w poprzedniej turze pierwszy w historii zestaw testów. W tej doszły testy silnika gier dla uszkodzonych i wrogich plików scen.

Sam fakt, że testy przechodzą, niewiele znaczy — test, który nie potrafi zawieść, jest gorszy niż jego brak, bo daje fałszywe poczucie bezpieczeństwa. Dlatego sprawdziliśmy to wprost: wstawiliśmy naprawione błędy z powrotem do kodu, po kolei, i za każdym razem patrzyliśmy, czy odpowiedni test się zapali. Zapaliły się wszystkie. Jeden wstrzyknięty błąd wywrócił nawet cały proces — dokładnie tak, jak powinien.

Przy okazji tej zabawy wyszła rzecz, której audyt nie widział. Zabezpieczenie przed nienormalnie zagnieżdżoną sceną nie wystarczało, bo program wywracał się wcześniej — już przy samym skopiowaniu danych z pliku, zanim zdążył cokolwiek sprawdzić. Bez próby złamania własnego zabezpieczenia nie mielibyśmy o tym pojęcia.


Trzy tury, jeden przegląd, kilkaset stron notatek. Aplikacja nie robi po nich nic nowego — robi to samo, tylko bez tych kilkudziesięciu miejsc, w których dotąd mijała się z prawdą albo cicho gubiła robotę. To nieefektowna praca i nie ma z niej zrzutu ekranu. Ale to jest różnica między narzędziem, którego się używa, a narzędziem, któremu się ufa.

📚 Related posts

27.08.2026 20:55

Data, której nie cofniemy nawet my

Każdy certyfikat autorstwa dostaje datę zapisaną w blockchainie Bitcoina — takiej nie podważy nikt, my też nie. Sprawdzisz ją bez konta i bez Moonligh...
Continue reading →
27.08.2026 19:35

Twój kolor, wszędzie

Trzy tryby i siedem akcentów, wspólnych dla strony i Moonlight Animate. Motyw jedzie z kontem, profil pokazuje gościom Twoje kolory, a harmonogram sam...
Continue reading →
27.08.2026 18:31

Kto moderuje i na jakiej podstawie

Moderator to teraz osobna rola z własnym zakresem, a nie administrator w przebraniu. Odrzucenie treści wymaga powodu, który trafia do autora razem z d...
Continue reading →

📧 Stay up to date!

Join the waitlist to get notified about new Dev Blog posts and Moonlight platform launch.