Integration Guide

Updated 9 hours 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!