Integration Guide

Updated 9 months ago

Integration Guide

Complete guide for integrating ZUI into your V Rising mod, including soft dependency patterns, reflection-based API access, and best practices.


📋 Table of Contents


Integration Methods

There are two ways to integrate ZUI into your mod:

Method Pros Cons Recommended
Soft Dependency Works with/without ZUI, no crashes, max compatibility Requires reflection, slightly more code ✅ YES
Hard Dependency Direct API access, cleaner code Mod fails without ZUI, compatibility issues ❌ NO

Soft Dependency (Recommended)

Soft dependencies allow your mod to work whether ZUI is installed or not. This is the best practice for V Rising mods.

Why Soft Dependencies?

✅ Your mod always loads - Even if ZUI isn't installed

✅ No crashes - Gracefully handles missing ZUI

✅ Better compatibility - Works on servers without ZUI

✅ User friendly - Users don't need to install multiple mods

✅ Flexible - UI features are optional, not required

Basic Implementation

Here's the complete pattern for soft dependency integration:

using BepInEx;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using System;
using System.Linq;
using System.Reflection;

namespace YourMod
{
    [BepInPlugin("com.yourname.yourmod", "Your Mod", "1.0.0")]
    [BepInDependency("Zanakinz.ZUI", BepInDependency.DependencyFlags.SoftDependency)]
    public class Plugin : BasePlugin
    {
        public static ManualLogSource LogInstance { get; private set; }
        private static Type _zui;

        public override void Load()
        {
            LogInstance = Log;
            Log.LogInfo("Loading Your Mod...");

            // Try to initialize ZUI
            if (InitZUI())
            {
                Log.LogInfo("ZUI detected! Registering UI...");
                RegisterUI();
            }
            else
            {
                Log.LogWarning("ZUI not found. UI features disabled.");
            }

            // Your mod's other initialization here
            // This code runs whether ZUI is present or not
        }

        private bool InitZUI()
        {
            // Check if ZUI plugin is loaded
            if (!IL2CPPChainloader.Instance.Plugins.ContainsKey("Zanakinz.ZUI"))
            {
                return false;
            }

            // Find ZUI assembly
            var assembly = AppDomain.CurrentDomain.GetAssemblies()
                .FirstOrDefault(a => a.GetName().Name == "ZUI");
            
            if (assembly == null)
            {
                return false;
            }

            // Get the ZUI API type
            _zui = assembly.GetType("ZUI.API.ZUI");
            
            return _zui != null;
        }

        private void Call(string methodName, params object[] args)
        {
            if (_zui == null) return;

            // Find the method with matching name and parameter count
            var method = _zui.GetMethods(BindingFlags.Public | BindingFlags.Static)
                .FirstOrDefault(m => m.Name == methodName && 
                                     m.GetParameters().Length == args.Length);

            if (method != null)
            {
                method.Invoke(null, args);
            }
            else
            {
                LogInstance.LogError($"Could not find ZUI method '{methodName}' with {args.Length} parameters.");
            }
        }

        private void RegisterUI()
        {
            // Set your plugin context
            Call("SetPlugin", "Your Mod");
            
            // Target main menu
            Call("SetTargetWindow", "Main");
            
            // Add UI elements
            Call("AddCategory", "Features");
            Call("AddButton", "Test", ".test");
            
            LogInstance.LogInfo("UI registered successfully!");
        }
    }
}

How It Works

  1. BepInDependency with SoftDependency flag - Tells BepInEx this mod optionally uses ZUI
  2. InitZUI() - Checks if ZUI is loaded and gets the API type via reflection
  3. Call() helper method - Invokes ZUI methods dynamically
  4. Conditional registration - Only creates UI if ZUI is present
  5. Graceful fallback - Mod continues to work without ZUI

Hard Dependency (Not Recommended)

Hard dependencies require ZUI to be installed, or your mod won't load.

⚠️ Important Warning

ZUI is client-side while most V Rising mods are server-side. Mixing client and server dependencies causes:

  • ❌ Mod fails to load on servers without ZUI
  • ❌ Compatibility issues with other mods
  • ❌ Load order problems
  • ❌ Bad user experience (forced to install ZUI)

Only use hard dependencies if:

  • Your mod is purely client-side
  • You absolutely need ZUI for core functionality
  • You understand the compatibility implications

Hard Dependency Implementation

Plugin.cs:

using BepInEx;
using BepInEx.Unity.IL2CPP;
using ZUI.API;

namespace YourMod
{
    [BepInPlugin("com.yourname.yourmod", "Your Mod", "1.0.0")]
    [BepInDependency("Zanakinz.ZUI", BepInDependency.DependencyFlags.HardDependency)]
    public class Plugin : BasePlugin
    {
        public override void Load()
        {
            Log.LogInfo("Loading Your Mod...");
            
            // Direct API access (no reflection needed)
            ZUI.SetPlugin("Your Mod");
            ZUI.SetTargetWindow("Main");
            ZUI.AddCategory("Features");
            ZUI.AddButton("Test", ".test");
            
            Log.LogInfo("UI registered!");
        }
    }
}

YourMod.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <!-- Add ZUI as a compile-time reference -->
    <Reference Include="ZUI">
      <HintPath>path/to/ZUI.dll</HintPath>
      <Private>false</Private>
    </Reference>
    
    <!-- Other references... -->
  </ItemGroup>
</Project>

Drawbacks

  • Users must install ZUI or mod crashes
  • Server/client architecture conflicts
  • Harder to distribute and maintain
  • Less user-friendly

Recommendation: Use soft dependencies instead.


Understanding Client vs Server

This is critical for V Rising mod development.

ZUI is Client-Side

ZUI is a client-side UI framework. It runs on the player's game client and displays UI elements.

Client-side means:

  • Runs on the player's computer
  • Only affects that player's game
  • Cannot be installed on dedicated servers
  • Used for UI, visual effects, client data

Most V Rising Mods are Server-Side

Most gameplay mods are server-side and run on the dedicated server.

Server-side means:

  • Runs on the game server
  • Affects all players
  • Controls game logic, data, rules
  • Used for gameplay mechanics

The Problem with Mixing

When you add a client-side hard dependency to a server-side mod:

// This is problematic!
[BepInPlugin("com.yourname.servermod", "Server Mod", "1.0.0")]
[BepInDependency("Zanakinz.ZUI", BepInDependency.DependencyFlags.HardDependency)]
public class ServerMod : BasePlugin
{
    // Server logic here
}

What happens:

  • ❌ Mod won't load on dedicated servers (no ZUI installed)
  • ❌ Server crashes or refuses to start
  • ❌ All players lose server functionality

The Solution: Soft Dependencies

// This works!
[BepInPlugin("com.yourname.servermod", "Server Mod", "1.0.0")]
[BepInDependency("Zanakinz.ZUI", BepInDependency.DependencyFlags.SoftDependency)]
public class ServerMod : BasePlugin
{
    public override void Load()
    {
        // Server logic always runs
        InitializeServerFeatures();
        
        // UI only loads if player has ZUI installed
        if (InitZUI())
        {
            RegisterClientUI();
        }
    }
}

Result:

  • ✅ Server runs without ZUI (server-side features work)
  • ✅ Players with ZUI see UI (client-side enhancement)
  • ✅ Players without ZUI still play (no UI, but functional)
  • ✅ Maximum compatibility

Complete Implementation

Here's a production-ready implementation with all best practices:

Plugin.cs:

using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using System;
using System.Linq;
using System.Reflection;

namespace YourMod
{
    [BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
    [BepInDependency("Zanakinz.ZUI", BepInDependency.DependencyFlags.SoftDependency)]
    public class Plugin : BasePlugin
    {
        public static ManualLogSource LogInstance { get; private set; }
        private static Type _zuiType;
        private static bool _zuiAvailable;

        // Configuration
        private ConfigEntry<bool> _enableUI;

        public override void Load()
        {
            LogInstance = Log;
            Log.LogInfo($"{MyPluginInfo.PLUGIN_NAME} v{MyPluginInfo.PLUGIN_VERSION} loading...");

            // Load config
            _enableUI = Config.Bind("UI", "EnableUI", true, "Enable ZUI integration");

            // Initialize ZUI if available and enabled
            _zuiAvailable = InitZUI();
            
            if (_zuiAvailable && _enableUI.Value)
            {
                RegisterUI();
                Log.LogInfo("ZUI integration enabled");
            }
            else if (!_zuiAvailable)
            {
                Log.LogInfo("ZUI not available - UI features disabled");
            }
            else
            {
                Log.LogInfo("ZUI integration disabled by config");
            }

            // Your mod's core functionality here
            InitializeCore();
            
            Log.LogInfo($"{MyPluginInfo.PLUGIN_NAME} loaded successfully");
        }

        private bool InitZUI()
        {
            try
            {
                // Check if ZUI plugin is loaded
                if (!IL2CPPChainloader.Instance.Plugins.ContainsKey("Zanakinz.ZUI"))
                {
                    return false;
                }

                // Find ZUI assembly
                var assembly = AppDomain.CurrentDomain.GetAssemblies()
                    .FirstOrDefault(a => a.GetName().Name == "ZUI");
                
                if (assembly == null)
                {
                    Log.LogWarning("ZUI assembly not found");
                    return false;
                }

                // Get ZUI API type
                _zuiType = assembly.GetType("ZUI.API.ZUI");
                
                if (_zuiType == null)
                {
                    Log.LogWarning("ZUI.API.ZUI type not found");
                    return false;
                }

                return true;
            }
            catch (Exception ex)
            {
                Log.LogError($"Error initializing ZUI: {ex.Message}");
                return false;
            }
        }

        private void CallZUI(string methodName, params object[] args)
        {
            if (_zuiType == null) return;

            try
            {
                var method = _zuiType.GetMethods(BindingFlags.Public | BindingFlags.Static)
                    .FirstOrDefault(m => m.Name == methodName && 
                                         m.GetParameters().Length == args.Length);

                if (method != null)
                {
                    method.Invoke(null, args);
                }
                else
                {
                    LogInstance.LogWarning($"ZUI method not found: {methodName}({args.Length} params)");
                }
            }
            catch (Exception ex)
            {
                LogInstance.LogError($"Error calling ZUI.{methodName}: {ex.Message}");
            }
        }

        private void RegisterUI()
        {
            try
            {
                CallZUI("SetPlugin", MyPluginInfo.PLUGIN_NAME);
                CallZUI("SetTargetWindow", "Main");
                
                CallZUI("AddCategory", "My Features");
                CallZUI("AddButton", "Feature 1", ".feature1");
                CallZUI("AddButton", "Feature 2", ".feature2");
                
                // Optional: Custom window
                RegisterCustomWindow();
            }
            catch (Exception ex)
            {
                LogInstance.LogError($"Error registering UI: {ex.Message}");
            }
        }

        private void RegisterCustomWindow()
        {
            CallZUI("SetPlugin", MyPluginInfo.PLUGIN_NAME);
            CallZUI("SetTargetWindow", "MyPanel");
            CallZUI("SetUI", 600, 400);
            CallZUI("SetTitle", $"<color=#4ECDC4>{MyPluginInfo.PLUGIN_NAME}</color>");
            
            CallZUI("AddText", "Welcome!", 20f, 50f);
            CallZUI("AddButton", "Action", ".action", 20f, 100f);
        }

        private void InitializeCore()
        {
            // Your mod's main functionality
            // This runs whether ZUI is available or not
        }
    }
}

MyPluginInfo.cs:

namespace YourMod
{
    public static class MyPluginInfo
    {
        public const string PLUGIN_GUID = "com.yourname.yourmod";
        public const string PLUGIN_NAME = "Your Mod";
        public const string PLUGIN_VERSION = "1.0.0";
    }
}

Testing Your Integration

Test Scenarios

  1. With ZUI Installed

    • Load game
    • Check BepInEx console for "ZUI integration enabled"
    • Open ZUI main menu
    • Verify your buttons appear
  2. Without ZUI Installed

    • Remove ZUI.dll
    • Load game
    • Check console for "ZUI not available"
    • Verify mod still loads and functions
    • No errors in console
  3. ZUI Disabled in Config

    • Set EnableUI = false in config
    • Load game
    • Verify mod loads without UI

Common Test Cases

// Test: ZUI available
if (_zuiAvailable)
{
    Log.LogInfo("✓ ZUI integration working");
}

// Test: ZUI not available
if (!_zuiAvailable)
{
    Log.LogInfo("✓ Graceful fallback working");
}

// Test: Method calls don't crash
CallZUI("SetPlugin", "Test");
CallZUI("NonExistentMethod", "test"); // Should log warning, not crash

Troubleshooting

"ZUI not available" but ZUI is installed

Possible causes:

  • ZUI loaded after your mod (load order issue)
  • Wrong ZUI assembly name
  • ZUI version incompatibility

Solution:

// Add more detailed logging
Log.LogInfo($"Plugins loaded: {string.Join(", ", IL2CPPChainloader.Instance.Plugins.Keys)}");
var zuiAssembly = AppDomain.CurrentDomain.GetAssemblies()
    .FirstOrDefault(a => a.GetName().Name == "ZUI");
Log.LogInfo($"ZUI Assembly: {zuiAssembly?.FullName ?? "Not Found"}");

Methods not being called

Check:

  • Method name spelling matches exactly
  • Parameter count matches
  • ZUI type is not null

Add logging:

private void CallZUI(string methodName, params object[] args)
{
    Log.LogInfo($"Calling ZUI.{methodName} with {args.Length} args");
    // ... rest of method
}

Compile errors with hard dependency

Error: The type or namespace name 'ZUI' could not be found

Solution: Add ZUI.dll reference to .csproj:

<Reference Include="ZUI">
    <HintPath>path/to/ZUI.dll</HintPath>
    <Private>false</Private>
</Reference>

Mod crashes without ZUI (hard dependency)

This is expected behavior with hard dependencies.

Solution: Switch to soft dependency pattern shown above.


Best Practices Summary

✅ DO:

  • Use soft dependencies
  • Add error handling
  • Log initialization status
  • Test with and without ZUI
  • Make UI optional, not required
  • Use descriptive method names

❌ DON'T:

  • Use hard dependencies (unless purely client-side)
  • Assume ZUI is always available
  • Skip error handling
  • Forget to check _zuiType != null
  • Mix client/server dependencies

Related Pages


Ready to integrate? Start with the soft dependency pattern above and you'll be set!