Bloga Dön

Voxel Streams ve World Snapshots: Yüksek Performanslı bir User Generated Content Backend Mimarisi Mühendisliği

Yayınlanma tarihi 21 Nisan 2026
Voxel Streams ve World Snapshots: Yüksek Performanslı bir User Generated Content Backend Mimarisi Mühendisliği

Özet olarak

Bu makale, voxel tabanlı oyunlarda yüksek performanslı bir User Generated Content (UGC) Backend mimarisi için gereken karmaşık mühendisliği incelemektedir. Veri payload yönetimi ve egress maliyetleri gibi teknik engelleri ele alarak World Snapshots, Delta Compression ve Content-Addressable Storage gibi çözümler sunmaktadır. Geliştiriciler, bu modelleri uygulayarak veya horizOn gibi uzmanlaşmış platformları kullanarak ölçeklenebilir, topluluk odaklı oyun dünyaları oluşturabilirler.

Oyuncularınız voxel RPG oyununuzda yüzen bir katedral inşa etmek için 40 saat harcadı ve şimdi bunu 10.000 yabancıyla paylaşmak istiyorlar—backend sisteminiz 4 TB'lık egress faturasını ödemeye hazır mı? Çoğu geliştirici User-Generated Content (UGC) konusunu basit bir dosya yükleme problemi olarak görür, ancak Enshrouded gibi voxel tabanlı dünyalarla uğraşırken teknik gerçeklik çok daha karmaşıktır. Sadece bir dosya barındırmıyorsunuz; performanslı, uygun maliyetli ve güvenli kalması gereken bir distributed world-state synchronization sistemi tasarlıyorsunuz.

Indie Oyunların 'Karsinizasyonu': Oyundan Platforma Dönüş

Enshrouded'daki son 'Forging the Path' güncellemesi, oyuncuların dünya durumlarını paketleyip toplulukla paylaşmalarına olanak tanıyan Adventure Sharing özelliğini getiriyor. Bu hamle, sektörde genellikle 'Roblox olay ufku' olarak adlandırılan büyüyen bir trendi vurguluyor. Oyunlar artık statik deneyimler değil; yaratım için platformlar haline geliyorlar. Oyunların bu 'karsinizasyonu', türünüz ne olursa olsun, uzun vadeli Player Retention istiyorsanız, eninde sonunda bir user generated content backend mimarisinin teknik borcuyla uğraşmak zorunda kalacağınız anlamına gelir.

Voxellere büyük ölçüde güvenen Enshrouded gibi bir oyun için zorluk iki kat fazladır. Voxeller sonsuz yaratıcılığa ve tam yıkılabilirliğe izin verir, ancak devasa miktarda veri üretirler. Tek bir yüksek detaylı üs, 50 MB'lık ham voxel verisini kolayca aşabilir. Bunu 100.000 oyuncuyla çarptığınızda, sadece depolama maliyetleriniz bile oyun henüz Versiyon 1.0'a ulaşmadan stüdyonuzu bitirebilir.

Voxel Veri Payload Problemi

Bunun için bir backend'in nasıl tasarlanacağını anlamak için önce aslında neyin paylaşıldığına bakmamız gerekiyor. Bir voxel motorunda dünya tipik olarak chunklara bölünür (örneğin 16x16x16 veya 32x32x32). Her chunk voxel ID'lerini, ışık verilerini ve genellikle sandıklar veya üretim istasyonları gibi entityler için metadataları içerir.

Bir oyuncu 'bir macerayı paylaştığında', oyunun bir 'World Snapshot' alması gerekir. Bu sadece kayıt klasörünün kopyalanıp yapıştırılması değildir. Şunları gerektirir:

  1. Pruning: Paylaşılan sürümde olması gerekmeyen geçici verilerin (düşen eşyalar veya aktif canavar AI durumları gibi) kaldırılması.
  2. Serializing: Bellekteki octree veya ızgara yapılarının bir flat buffera dönüştürülmesi.
  3. Compression: Payloadu azaltmak için LZ4 veya Zstandard gibi algoritmaların uygulanması.

Bunu doğru yönetmezseniz, The Unreal Engine Multiplayer Sync Bug Ruining Your World States And How To Fix It makalesinde açıklanan, istemci ile paylaşılan snapshot arasındaki dünya durumu uyumsuzluklarının bozuk yapılara veya 'hayalet' voxellere yol açtığı aynı sorunlarla karşılaşırsınız.

Delta Compression: Sadece Değişeni Gönderin

UGC mimarisinde yaygın bir hata, bir oyuncu paylaştığı 'Adventure' üzerinde küçük bir güncelleme yaptığında her seferinde tüm dünya durumunu yüklemektir. Bunun yerine Delta Compression uygulamalısınız. Voxel chunklarının mevcut durumunu 'Base World' (prosedürel seed) ile karşılaştırarak sadece değişiklikleri (deltaları) saklamanız yeterlidir.

Temel dünya chunk (10, 5, 2)'nin katı bir dağ olduğunu söylüyorsa ve oyuncu oraya bir tünel kazdıysa, backend'inizin sadece 'Stone' voxellerinin yerini alan 'Air' voxellerini kaydetmesi gerekir. Bu, 10 MB'lık bir chunkı birkaç kilobayta düşürebilir.

Backend Pipeline'ını Mühendislikten Geçirmek

İstemci sıkıştırılmış bir snapshot veya delta oluşturduktan sonra backend devreye girer. Güçlü bir user generated content backend mimarisi üç ana katmandan oluşur: Ingestion Layer, Storage Layer ve Discovery Layer.

1. Ingestion Layer (Doğrulama ve Virüs Taraması)

İstemciye asla güvenmeyin. Kötü niyetli bir kullanıcı, aslında sıfırlarla dolu 2 GB'lık bir dosya (Zip Bomb) veya voxel deserializer'ınızı istismar etmek için tasarlanmış bir payload olan bir 'snapshot' yükleyebilir. Ingestion sunucunuz şunları yapmalıdır:

  • Tüm akışı kabul etmeden önce dosya başlığını ve boyutunu doğrulamalıdır.
  • Payloadu bir sandbox deserializer üzerinden çalıştırarak sunucuyu çökertmediğinden emin olmalıdır.
  • Yinelenen yüklemeleri önlemek için benzersiz bir Content Hash (SHA-256 gibi) oluşturmalıdır.

2. Storage Layer (Bloblar vs. Metadata)

Binary verilerinizi (voxel blobları) aranabilir verilerinizden (oyuncu adı, macera etiketleri, derecelendirme) ayırın.

  • Bloblar: S3 uyumlu nesne depolaması kullanın. Yüksek trafikli oyunlar için bu blobları edge'de önbelleğe almak üzere bir Content Delivery Network (CDN) kullanın.
  • Metadata: İndeksleme için PostgreSQL gibi bir ilişkisel veritabanı kullanın. Bu, oyuncuların 'High Fantasy' veya 'Level 10-20' maceralarını milisaniyenin altında gecikmeyle aramasına olanak tanır.

3. Discovery Layer (Gerçek Zamanlı Güncellemeler)

Yeni bir macera yayınlandığında, diğer oyuncuların bunu hemen görmesini istersiniz. İstemcilerin her 30 saniyede bir API'yi poll etmesi yerine, yeni içerik bildirimlerini iletmek için WebSockets kullanın. Bunu nasıl kuracağınızla ilgili derinlemesine bir inceleme için Ditch Http Polling An Unreal Engine Websockets Tutorial For Real Time Backends kılavuzumuza göz atın.

Uygulama: Bir C# UGC Yöneticisi Örneği

Aşağıda, Unity tabanlı bir oyunda UGC yükleme yöneticisini nasıl yapılandırabileceğinize dair basitleştirilmiş bir örnek bulunmaktadır. Bu kod, büyük voxel dosyalarının yavaş bağlantılarda zaman aşımına uğramamasını sağlamak için sıkıştırma ve multi-part yükleme işlemini gerçekleştirir.

using System;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;

public class AdventureUploader : MonoBehaviour
{
    private const string API_URL = "https://api.yourgame.com/v1/ugc/upload";

    public async Task ShareAdventure(string adventureId, byte[] rawVoxelData, AdventureMetadata metadata)
    {
        // 1. Bant genişliğinden tasarruf etmek için verileri yerel olarak sıkıştırın
        byte[] compressedData = await CompressData(rawVoxelData);
        Debug.Log($"Compressed world state from {rawVoxelData.Length / 1024}KB to {compressedData.Length / 1024}KB");

        // 2. Multi-part form oluşturun
        WWWForm form = new WWWForm();
        form.AddField("adventureId", adventureId);
        form.AddField("title", metadata.Title);
        form.AddBinaryData("worldBlob", compressedData, "world.vox", "application/octet-stream");

        // 3. Backend'e gönderin
        using (UnityWebRequest www = UnityWebRequest.Post(API_URL, form))
        {
            var operation = www.SendWebRequest();
            while (!operation.isDone) await Task.Yield();

            if (www.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError($"UGC Upload Failed: {www.error}");
            }
            else
            {
                Debug.Log("Adventure shared successfully!");
            }
        }
    }

    private async Task<byte[]> CompressData(byte[] data)
    {
        using (var outputStream = new MemoryStream())
        {
            using (var gZipStream = new GZipStream(outputStream, CompressionMode.Compress))
            {
                await gZipStream.WriteAsync(data, 0, data.Length);
            }
            return outputStream.ToArray();
        }
    }
}

[Serializable]
public class AdventureMetadata
{
    public string Title;
    public string Description;
    public string CreatorId;
}

Kendi Başınıza Yapmanın Maliyeti

Bu pipeline'ı manuel olarak tasarlamak önemli bir iştir. Şunları yönetmeniz gerekir:

  • Load Balancers: Ünlü bir streamer bir macerayı paylaştığında oluşacak trafik artışlarını karşılamak için.
  • Database Sharding: Metadata tablonuz milyonlarca satıra ulaştığında.
  • CDN Invalidations: Bir oyuncu macerasını güncellediğinde eski sürümün önbellekten sunulmamasını sağlamak.

Bunu kendiniz inşa etmek genellikle 2-3 aylık özel senior mühendislik zamanı gerektirir. İşte bu noktada horizOn büyük değer sağlar. Binary blob depolama ve metadata indeksleme için 'tesisatı' inşa etmek yerine, horizOn önceden tasarlanmış bir UGC modülü sunar. Siz sadece metadata şemanızı tanımlarsınız ve horizOn küresel dağıtımı, dosya doğrulamasını ve ölçeklendirmeyi otomatik olarak halleder. Bu, S3 bucket politikaları hakkında endişelenmek yerine 'Adventure' oynanışını eğlenceli hale getirmeye odaklanmanızı sağlar.

UGC Backend Mimarileri İçin 5 En İyi Uygulama

  1. Content-Addressable Storage (CAS) Uygulayın: Voxel verilerinin hash'ini dosya adı olarak kullanın. İki oyuncu aynı üsleri paylaşıyorsa, fiziksel dosyayı yalnızca bir kez saklarsınız, bu da devasa miktarda depolama alanı tasarrufu sağlar.
  2. Asenkron Ingestion Kullanın: Oyuncuyu backend'in dosyayı işlemesini beklemesi için bekletmeyin. Hemen '202 Accepted' döndürün ve doğrulama ile küçük resim oluşturma işlemlerini halletmek için bir background worker kullanın.
  3. Tiered Storage Stratejisi: En popüler 100 macerayı 'Hot' bir önbellekte (Redis/CDN) tutun ve maliyetleri düşük tutmak için daha eski, oynanmayan maceraları 'Cold' depolamaya (S3 Glacier) taşıyın.
  4. Şemayı Versiyonlayın: Oyununuzu güncelledikçe voxel formatınız değişecektir. Backend'iniz her yüklemede bir format_version integer'ı saklamalıdır, böylece eski maceralar sorunsuz bir şekilde taşınabilir veya kullanımdan kaldırılabilir.
  5. Her Şeyi Rate Limit'e Tabii Tutun: UGC, bir oyun sunucusuna DDoS saldırısı yapmanın en kolay yoludur. Tek bir IP veya PlayerID'nin ne kadar sıklıkla içerik yükleyebileceği veya arayabileceği konusunda katı hız sınırları uygulayın.

Sonuç: Paylaşılan Dünyaların Geleceği

Enshrouded'ın gösterdiği gibi, indie oyunlar için teknik çıta yükseliyor. Oyuncular yaratımlarını sorunsuz bir şekilde paylaşabilmeyi bekliyor ve 'Adventure Sharing' bir lüks olmaktan çıkıp hızla standart bir özellik haline geliyor. Geliştirmenin erken aşamalarında sağlam bir user generated content backend mimarisine odaklanarak, topluluğunuz altyapınızı aştığında ortaya çıkan sancılı refactorlerden kaçınmış olursunuz.

Ham sunucuları yönetme zahmetine girmeden multiplayer backend'inizi ölçeklendirmeye hazır mısınız? horizOn'u ücretsiz deneyin veya yüksek hacimli UGC ve dünya durumu kalıcılığını nasıl yönettiğimizi görmek için API dokümanlarına göz atın.


Kaynak: Enshrouded approaches the Roblox event horizon with the addition of Adventure Sharing in its latest update