Hayır diyen bir çalışma zamanını simüle etmek: tarayıcıyı WeChat gibi davranacak şekilde nasıl korumalı alana alıyoruz
Cinevva CTO'su ve Kurucu Ortağı Oleg Sidorkin tarafından

Yakın zamanda Game Creator'a bir WeChat mini oyun modu ekledik: Yapay zekâ tarafından üretilen oyunları, web platformunun WeChat çalışma zamanına taşındığında çalışmaya devam eden alt kümesi içinde tutan bir derleme profili. Profil, bir dizi üretim kuralından oluşuyor. DOM tabanlı kullanıcı arayüzü yok, Pointer Lock yok, yalnızca mp3 ses, dinamik kod yok. Model bu kurallara, modellerin kurallara uyduğu şekilde uyuyor; yani genellikle.
Her ihlal tarayıcıda görünmezken dışa aktarımdan sonra ölümcül oluyorsa "genellikle" yeterli değildir. HTML sağlık çubuğu olan bir oyun önizlememizde kusursuz çalışır, ancak WeChat mini oyunlarında hiç DOM bulunmadığı için WeChat'te boş ekran üretir. Bu nedenle söz konusu kuralları öneriden fiziğe dönüştüren şeyi geliştirdik: Tarayıcının, WeChat'in yapamadığı şeyleri yapmayı reddetmesini sağlayan katı modda bir korumalı alan. Bu yazı, onun nasıl çalıştığını anlatıyor.
Temel karar: varlığı değil, yokluğu simüle etmek
Bariz yaklaşım WeChat'i taklit etmek olurdu: wx.createCanvas, wx.onTouchStart, wx.setStorageSync işlevlerini uygulamak ve oyunları taklit edilen wx.* yüzeyinde çalıştırmak. Biz bunun tersini yaptık ve bunun nedeni dışa aktarma işlem hattımızın yapısı.
WeChat modundaki oyunlar hâlâ tarayıcı API'lerine göre yazılıyor. Paketleme sırasında bir adaptör (topluluk standardı weapp-adapter yaklaşımı) bu tarayıcı API'lerini wx.* üzerine eşliyor. Bu, oyunlarımızın hiçbir zaman doğrudan wx.* çağırmadığı anlamına geliyor; dolayısıyla bir wx emülatörü, var olmayan kod yollarını test ederdi. Bir taşıma işlemini asıl bozan şey, tarayıcıda wx.* bulunmaması değildir. Üretim sırasında sessizce kullanılan, WeChat'te karşılığı olmayan tarayıcı API'lerinin varlığıdır. document.createElement('div'). requestPointerLock(). IndexedDB. Chrome'da sorunsuz çözülen ancak WeChat iOS'ta hiçbir zaman çözülmeyecek bir .ogg dosyası.
Bu nedenle korumalı alan yokluğu simüle ediyor. WeChat'te bulunmayan her şey önizlemede kaldırılıyor, engelleniyor veya işaretleniyor; böylece oyun daha ilk karesinden itibaren iki platformun kesişim kümesi içinde geliştiriliyor.
Nerede çalışıyor: zaten mevcut olan bir service worker
Editör önizlememizin alışılmadık sunum yolu, bunu neredeyse bedavaya getirdi. Editördeki oyunlar ağ üzerinden sunulmuyor. Bir service worker, /game/* isteklerini yakalayıp dosyaları IndexedDB'den sunuyor; önizleme iframe'inin ağ gidiş dönüşü olmadan anında yeniden yüklenmesini sağlayan da bu. Bu worker zaten her oyun sayfasına iki sanal dosya enjekte ediyor: konsol çıktısını ve çökmeleri editöre ileten bir hata yakalama betiği ile canlı sahne inceleyicisi.
Korumalı alan, üçüncü sanal dosya olan _wechat-strict.js; yakalama betiğinden hemen sonra ve herhangi bir oyun kodu çalışmadan önce sayfanın <head> bölümüne enjekte ediliyor. Sıralama iki açıdan kritik. console.error çağrılarının önceden yakalanıp editöre akabilmesi için yakalama betiğinden sonra, engellerin ilk oyun kodu satırı çalıştırıldığında devrede olması için de oyun modüllerinden önce çalışmalı.
Etkinleştirme tek satırlık bir numaraya dayanıyor. Oluşturucudaki WeChat anahtarı localStorage içinde saklanıyor ve önizleme iframe'i editörle aynı origin'i kullandığından test düzeneği bayrağı doğrudan okuyabiliyor:
try {
if (localStorage.getItem('cinevva-gc-profile') !== 'wechat-minigame') return;
} catch (e) { return; }WeChat dışındaki her oyun için betik yalnızca tek bir dize karşılaştırması yapıp erkenden çıkıyor. Derleme bayrağı yok, sunucu gidiş dönüşü yok, service worker ile eşitlenecek durum yok.
Engelleme listesi ve iki katmanlı sınıflandırma
Her ihlal aynı tepkiyi hak etmiyor; bu nedenle test düzeneği iki katmanı birbirinden ayırıyor. WeChat'te hiç bulunmayan API'ler hata fırlatıyor; çünkü dışa aktarımdan sonra olan da bu ve üretim sırasında yaşanan bir çökme, inceleme sırasında yaşanacak çökmenin dürüst bir önizlemesidir. Var olup daha sonra bozulan şeyler ise davranışları değiştirilmeden yüksek sesli konsol hataları olarak işaretleniyor; çünkü bunları engellemek, oyunun gerçek durumunu üzerinde çalışan kişiden gizlerdi.
Hata fırlatma katmanı: Hem createElement hem de createElementNS üzerinden herhangi bir HTML kullanıcı arayüzü öğesi oluşturmak (Three.js kendi canvas'ını NS varyantıyla oluşturduğu için her iki yolun da aynı kapıdan geçmesi gerekiyor), eval, new Function, requestPointerLock ve service worker kaydı. Fırlatılan her hata, mesajında çözümü de taşıyor:
throw violate("document.createElement('" + tag + "') — WeChat mini oyunlarında DOM bulunmaz. " +
"Kullanıcı arayüzünü canvas üzerine çizin (ekran dışı 2D canvas -> ekran uzayında bir dörtgen üzerinde THREE.CanvasTexture) " +
"ve dokunma isabet testini kendiniz yapın.");İşaretleme katmanı: indexedDB okuma (tam WeChat'teki gibi undefined döndürür ve ayrıca localStorage kullanımını öneren bir hata verir), WeChat iOS'un çözemediği herhangi bir biçimdeki sesler (üç yerde kontrol edilir: Audio kurucusu, medya öğesi prototipindeki src ayarlayıcısı ve getirilen URL'ler), oyunun kendi origin'i veya varlık CDN'imiz dışındaki herhangi bir origin'e yapılan ağ istekleri (WeChat'te bunlar beyaz listeye alınmış, ICP kaydı yapılmış bir alan adı gerektirir) ve Tone.js'in herhangi bir şekilde ortaya çıkması.
Ayrıca sayfa yerleştikten kısa süre sonra çalışan bir yükleme sonrası denetim de var: <body> içinde gezinir ve dinamik oluşturma yerine oyunun kendi işaretlemesiyle gelen tüm HTML öğelerini bildirir. Statik HUD'lar bir createElement kapısından kaçabildiği için yalnızca kapı yeterli değildir.
Her rapor mesaj bazında yinelenen kayıtları kaldırıyor ve oturum başına elli raporla sınırlandırılıyor. Her karede bir div oluşturan ters çevrilmiş döngü hatası, sinyali boğan bir hata seli değil, tek bir hata üretiyor.
Bize en çok şey öğreten dört istisna
Korumalı alan oluşturmak çoğunlukla neyin engellenmeyeceğine karar vermektir ve eklediğimiz her istisna, geliştirme sırasında kendi araçlarımızın bozulmasından doğdu.
Hata ayıklayıcımız eval üzerinde çalışıyor. Yapay zekânın çalışan oyunu incelemek için kullandığı execute_js aracı, kodu eval çağıran yakalama betiğinin mesaj işleyicisi üzerinden değerlendiriyor. eval işlevini safça engellemek, yapay zekânın kendi gözlerini kör ederdi. Çözüm: Test düzeneği, engellemeden önce gerçek işlevi numaralandırılamayan bir özellikte saklıyor ve yakalama betiğinin işleyicisi gerektiğinde buna geri dönüyor:
Object.defineProperty(window, '__cinevvaRealEval', { value: window.eval, enumerable: false });
window.eval = function () { throw violate('WeChat mini oyunlarında eval() yasaktır.'); };
// capture script, at message time:
returnValue = (window.__cinevvaRealEval || eval)(e.data.code);eval kullanmaya çalışan oyun kodu yine duruyor. Hata ayıklayıcı ise çalışmaya devam ediyor.
Kaydedici de öğeler oluşturuyor. Reel kaydedicimiz, iframe'in kendi document.createElement işleviyle oluşturulan bir <script> olarak oyun iframe'ine enjekte ediliyor ve tamamlanan videoları sentetik bir <a> tıklaması aracılığıyla indiriyor. Bu etiketleri engellemek, kayıt özelliğini yalnızca ve tam olarak WeChat modunda bozardı. Bu nedenle script etiketine izin veriliyor ve a hata fırlatmadan uyarı veriyor. Gerçek bir bağlantıyla yayımlanan oyun yine işaretleniyor, araçlar da çalışmaya devam ediyor.
Klavye olayları kalıyor. Yapay zekâ, tuş basımlarını taklit eden ve oyun durumunun nasıl değiştiğini geri okuyan bir araçla kontrolleri doğruluyor. Telefonu simüle etmek için klavye girişini bastırmak, ters çevrilmiş kamera hatalarını yakalayan kontrol doğrulama döngüsünü yok ederdi. Önceliğin dokunmatik kontrollerde olması korumalı alan tarafından değil, üretim kuralları ve profilin kontrol şeması tarafından zorunlu tutuluyor.
WebGL2 kalıyor ve nedeni aşağıda kendine ait bir bölümü hak ediyor. Engelleme listesinin taslağı, profilin talep ettiği "WebGL1 uyumlu" işlemeyi zorlamak için getContext('webgl2') çağrısını engelliyordu. Ancak Three.js r163 sürümünde WebGL1 desteğini tamamen kaldırdı ve biz r181 sürümünü sabitliyoruz: WebGL2'yi engellemek bu moddaki her 3D oyunu boş ekrana dönüştürürdü. Profilin işleme kuralı tutarsızdı ve platforma dair güncelliğini yitirmiş bir zihinsel modele göre yazılmıştı. Korumalı alan denetimi, korumalı alanın kendi teknik şartnamesini denetlemiş oldu ve bu ipin ucunu çekmek bizi önemli bir yere götürdü.
WebGL2 sorusunun doğru yanıtı
Bağlam oluşturma işlemini olduğu gibi bırakmak, bariz bir devam sorusu doğurdu: Motorumuz WebGL2 gerektiriyorsa oyunlar yalnızca WebGL1'e sahip WeChat cihazlarında bozulur mu? Bu sorunun peşine düşmek, platformun minimum işleme kapasitesi hakkında şimdiye kadarki en net tabloyu ortaya çıkardı; kaynaklarıyla birlikte durum şöyle.
Android'de WeChat'in mini oyun çalışma zamanı, güncel herhangi bir temel kitaplıkta WebGL2 sunuyor ve alttaki GLES3 donanımı fiilen evrensel durumda. Asıl mesele iOS. WeChat'in WebGL2 desteğine ve yüksek performans+ moduna ilişkin kendi mühendislik belgeleri, iPhone'da WebGL2'nin yalnızca güncel bir WeChat istemcisi (8.0.45 ve üzeri, iOS 14'te daha yüksek) ve fiilen iOS 15.5 veya üzerini gerektiren yüksek performans+ çalışma zamanında düzgün biçimde kullanılabildiğini açıkça belirtiyor. Tencent'in kendi tanımına göre düz yüksek performans modu WebGL2'yi "daha fazla sorunla" çalıştırıyor ve normal iOS çalışma zamanında WebGL2 hiç bulunmuyor.
Asıl tehlikeli olan, hatanın ortaya çıkış biçimi. Desteklenmeyen ortamlarda getContext('webgl2'), null yerine truthy ama bozuk bir bağlam döndürebiliyor; geliştiriciler Tencent'ten doğrudan düzeltmesini istediği bir davranış bu. Dönüş değerine güvenen bir oyun burada temiz biçimde hata vermiyor. Bozuk grafikler veya sessiz bir siyah ekran oluşturuyor.
Bunu üç katmanda ele alıyoruz. Korumalı alan, kendi motorumuzla çatışacağı için WebGL2 bağlam oluşturma işlemini olduğu gibi bırakıyor. Üretim profili artık her oyunda bir açılış kapısı bulunmasını şart koşuyor: Renderer oluşturma işlemi try/catch içine alınıyor, ardından işlevsel bir kontrol yapılıyor (typeof gl.createVertexArray === 'function'; yalancı bir bağlamda bulunmayacak, yalnızca WebGL2'ye özgü bir özellik) ve başarısızlık durumunda boş ekran yerine canvas üzerine çizilmiş, kullanıcı dostu bir "lütfen WeChat'i güncelleyin" mesajı gösteriliyor. Bu, Unity'den dönüştürülen mini oyunların kullandığı kalıbın aynısı; bu yüzden gerçek dünyada siyah ekranlar yerine yükseltme istemleri görüyorsunuz. Dışa aktarıcı hazır olduğunda da yetenekli yolun varsayılan olması için game.json içinde yüksek performans+ modunu sabitleyecek.
WebGL2 taban çizgisinin kitle üzerindeki maliyeti nedir? Yaklaşık olarak iOS 15.5'in altındaki veya 8.0.45'ten eski WeChat istemcilerini kullanan kullanıcılar; 2026'da düşük tek haneli yüzdeler, ancak eski cihazlarda yoğunlaşıyor ve bu bazı oyun türleri için diğerlerinden daha önemli. Gerçek dağıtım verileri bir gün bu uzun kuyruğun peşinden gitmeye değdiğini gösterirse ucuz bir kaçış yolunu hazır tutuyoruz: WeChat profilini WebGL1'i destekleyen son sürüm olan three r162'ye sabitlemek. Profil oyunları yalnızca kararlı temel API'lere dokunduğu için sürüm düşürmek, veriler gerektirene kadar bilinçli olarak kullanılmadan bekletilen tek satırlık bir import map değişikliğinden ibaret.
Model ile döngüyü kapatmak
Yapay zekâ öncelikli bir ürün için bunu geliştirmeye değer kılan kısım şu. Her derlemeden sonra oluşturucunun ajanı, oyunun konsolunu okuyan ve açılışın tamamlanmasını bekleyen bir aracı çağırıyor. Yakalama betiği console.error çıktılarını bu akışa iletiyor. Dolayısıyla bir [wechat-strict] ihlali, insanın kaydırıp geçebileceği bir uyarı değildir. Modelin bir derlemenin tamamlandığını ilan etmeden önce zaten kontrol ettiği kanala, aynı tur içinde ve çözümü mesajda açıkça yazılmış hâlde ulaşır.
Üretim profili son boşluğu tek bir talimatla kapatıyor: Her [wechat-strict] mesajını derlemeyi bozan bir hata olarak ele al, nedenini düzelt ve test düzeneğini hiçbir zaman tespit etmeye veya atlatmaya çalışma. Yalnızca korumalı alan kapalıyken çalışan bir oyun, dışa aktarımdan sonra başarısız olacak bir oyundur ve modele tam olarak bu söylenir.
Korumalı alan böylece muğlak bir uyumluluk sorununu ("model on dört kuralın tümüne uydu mu?"), sistemin zaten iyi olduğu hata ayıklama döngüsüne ("konsolda bir hata var, düzelt") dönüştürüyor.
Tarayıcının simüle edemeyecekleri
Dürüstlük bölümü. Bu korumalı alan, tahminimize göre bir taşıma işlemini bozan sorunların büyük çoğunluğunu oluşturan API yüzeyi ihlallerini yakalıyor. Şunları yakalayamaz: gerçek bir telefondaki performans, iOS'un JavaScript'i JIT olmadan çalıştırması, WeChat'in WebGL uygulamasına özgü tuhaflıklar veya dosya uzantılarının ötesindeki codec davranışı farkları. Bunlar gerçek çalışma zamanını gerektiriyor.
Bu nedenle korumalı alan üç halkanın ilki. İkinci halka, dışa aktarıcımız WeChat DevTools projeleri üretmeye başladığında, DevTools CLI ve miniprogram-automator aracılığıyla başsız olarak yönetilen resmî simülatör olacak: Dışa aktarılan paketi başlat, ilk karenin işlendiğini doğrula, bir dokunma işlemini betikle. Üçüncü halka ise önizleme QR kodları ve fiziksel telefonlarda uzaktan hata ayıklamadan oluşan resmî cihaz yolu; başka hiçbir şeyin ortaya çıkaramadığı gerçekler burada yaşar. Her halka öncekinden daha yavaş ve gerçeğe daha yakındır; her birinin görevi, bir sonrakine gitme ihtiyacını azaltmaktır.
Modu denemek isterseniz Game Creator içindeki "WeChat mini oyun modu" anahtarını kullanabilirsiniz. Tüm bunların pazar ve mevzuat bağlamı için WeChat'in yarım milyar oyuncusuna ulaşma saha rehberimize göz atın.
Kaynaklar
- WeChat resmî belgeleri: Mini oyunlarda JavaScript desteği
- WeChat resmî belgeleri: Adaptör katmanı
- WeChat resmî belgeleri: Yüksek performans+ modu
- WeChat mühendislik belgeleri: Mini oyunlarda WebGL2 işleme desteği
- WeChat mühendislik belgeleri: iOS yüksek performans ve yüksek performans+ modları
- WeChat geliştirici topluluğu: Bozuk webgl2 bağlamları null döndürmelidir
- WeChat resmî belgeleri: miniprogram-automator
- Three.js r163 sürüm notları (WebGL1 desteği kaldırıldı)