How to Make Custom Bannerlord Scripts Run on a Multiplayer Server

Users who are viewing this thread

duck123

Recruit
Here is a video I made that talks about getting custom scripts in the scene editor to work on a multiplayer server. Ive attached the code examples from the video. I made a similar post earlier for getting scripts to run just in single player: https://forums.taleworlds.com/index...s-to-use-in-scene-editor.454338/#post-9833789.


You must add the module to your start server command: .\DedicatedCustomServer.Starter.exe /dedicatedcustomserverconfigfile ds_config_duel_Pontoon.txt _MODULES_*Native*Multiplayer*MyMods*_MODULES_

You must put your module dll in the right location: C:\Program Files (x86)\Steam\steamapps\common\Mount & Blade II Dedicated Server\Modules\MyMods\bin\Win64_Shipping_Server

Here is my submodule.xml from my server as an example of how to set that for the custom script dll.

XML:
<Module>
    <Name value="MyMods"/>
    <Id value="MyMods"/>
    <Version value="v1.0.2"/>
    <DefaultModule value="false"/>
    <ModuleCategory value="Multiplayer"/>
    <Official value="false"/>
    <DependedModules>
        <DependedModule Id="Native" DependentVersion="v1.0.2" Optional="false"/>
    </DependedModules>
    <SubModules>
        <SubModule>
            <Name value="MyMods"/>
            <DLLName value="MyMods.dll"/>
            <SubModuleClassType value="MyMods.Main"/>
            <Tags>
                <Tag key="DedicatedServerType" value="custom" />
            </Tags>
        </SubModule>
    </SubModules>
    <Xmls>
    </Xmls>
</Module>

C#:
using NetworkMessages.FromServer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TaleWorlds.Engine;
using TaleWorlds.Library;
using TaleWorlds.MountAndBlade;

namespace MyMods
{
    public class CS_MoveBackAndForth : SynchedMissionObject
    {
        public bool EnabledInGame = false;
        public bool EnabledInEditor = false;
        public float Destination_X1 = 0f;
        public float Destination_Y1 = 0f;
        public float Destination_Z1 = 0f;
        public float Destination_X2 = 0f;
        public float Destination_Y2 = 0f;
        public float Destination_Z2 = 0f;
        public float MoveSpeedX = 0f;
        public float MoveSpeedY = 0f;
        public float MoveSpeedZ = 0f;
        public float SlowAtDist = 0f;
        public float SlowSpeedPercent = 0f;
        private bool FirstRun = true;
        private bool GoToDest1 = true;
        private bool DestXGreater = true;
        private bool DestYGreater = true;
        private bool DestZGreater = true;
        private float sixtyFpsFrameTime = 0.0166f;
        private SynchedMissionObject Platform;
        private bool editorEntitySet = false;

        private void Move(float dt)
        {
            float frameTime = dt;


            // We need to compensate for frame differences vs my standard 60 fps. Need to figure out what fps the server uses.
            float movementFrameModifier = frameTime / sixtyFpsFrameTime;

            if (GoToDest1 == true)
            {
                MakeMove(Destination_X1, Destination_Y1, Destination_Z1, movementFrameModifier);
            }
            else
            {
                MakeMove(Destination_X2, Destination_Y2, Destination_Z2, movementFrameModifier);
            }

        }

        private void MakeMove(float Destination_X, float Destination_Y, float Destination_Z, float movementFrameModifier)
        {
            float curPositionX = this.Platform.GameEntity.GetFrame().origin.x;
            float curPositionY = this.Platform.GameEntity.GetFrame().origin.y;
            float curPositionZ = this.Platform.GameEntity.GetFrame().origin.z;
            bool reachedDestination = false;
            float movementX = MoveSpeedX * movementFrameModifier, movementY = MoveSpeedY * movementFrameModifier, movementZ = MoveSpeedZ * movementFrameModifier;

            // Set compare types for first run in current route to destination
            if (FirstRun)
            {
                if (curPositionX >= Destination_X)
                {
                    DestXGreater = false;
                }
                if (curPositionY >= Destination_Y)
                {
                    DestYGreater = false;
                }
                if (curPositionZ >= Destination_Z)
                {
                    DestZGreater = false;
                }
                FirstRun = false;
            }
            /////////////////////////////////////

            // Do compares based on compare types we set to see what direction we move or if we are done

            movementX = DetermineMovement(movementX, curPositionX, Destination_X, DestXGreater);
            movementY = DetermineMovement(movementY, curPositionY, Destination_Y, DestYGreater);
            movementZ = DetermineMovement(movementZ, curPositionZ, Destination_Z, DestZGreater);

            ///////////////////////////////////////////////////

            // Check if we have reached destination
            if (movementX == 0f && movementY == 0f && movementZ == 0f)
            {
                reachedDestination = true;
            }
            //////////////////////////////////////////////

            // If we have reached our destination then setup for going to the other destination and return
            if (reachedDestination == true)
            {
                GoToDest1 = !GoToDest1;
                FirstRun = true;

                DestXGreater = true;
                DestYGreater = true;
                DestZGreater = true;

                return;
            }
            //////////////////////////////////////////////////////////////

            // Do the actual vector move for the entity
            Vec3 vec = new Vec3(movementX, movementY, movementZ, -1f);
            MatrixFrame frame = this.Platform.GameEntity.GetFrame();
            frame.origin += vec;

            if (GameNetwork.IsServer)
            {
                this.Platform.SetFrameSynched(ref frame, false);
            }
            else
            {
                this.Platform.GameEntity.SetFrame(ref frame);
            }

        }

        private float DetermineMovement(float movement, float curPosition, float destination, bool DestGreater)
        {

            if (DestGreater == true)
            {
                if (destination <= curPosition)
                {
                    movement *= 0f;
                }
                else if (destination <= (curPosition + SlowAtDist))
                {
                    movement *= SlowSpeedPercent;
                }
            }
            else
            {
                movement *= -1f;
                if (destination >= curPosition)
                {
                    movement *= 0f;
                }
                else if (destination >= (curPosition - SlowAtDist))
                {
                    movement *= SlowSpeedPercent;
                }
            }

            return movement;
        }

        public bool NetworkLog(NetworkCommunicator networkPeer, string message)
        {
            GameNetwork.BeginModuleEventAsServer(networkPeer);
            GameNetwork.WriteMessage(new ServerMessage(message));
            GameNetwork.EndModuleEventAsServer();
            return true;
        }

        public override ScriptComponentBehavior.TickRequirement GetTickRequirement()
        {
            return ScriptComponentBehavior.TickRequirement.Tick | base.GetTickRequirement();
        }

        protected override void OnInit()
        {
            base.OnInit();
            base.SetScriptComponentToTick(this.GetTickRequirement());

            GameEntity tempEntity = this.GameEntity.CollectChildrenEntitiesWithTag("child_mover").SingleOrDefault<GameEntity>();
            if (tempEntity != null)
            {
                this.Platform = tempEntity.GetScriptComponents<SynchedMissionObject>().SingleOrDefault<SynchedMissionObject>();
            }

        }

        protected override void OnTick(float dt)
        {

            if(this.Platform == null)
            {
                return;
            }

            if (EnabledInGame == true)
            { 
                if (GameNetwork.IsMultiplayer)
                {
                    if (GameNetwork.IsServer)
                    {
                        this.Move(dt);
                    }
                }
                else
                {
                    this.Move(dt);
                }
              
            }

        }

        protected override void OnEditorTick(float dt)
        {
            base.OnEditorTick(dt);
            //InformationManager.DisplayMessage(new InformationMessage("step 0"));

            GameEntity tempEntity = this.GameEntity.CollectChildrenEntitiesWithTag("child_mover").SingleOrDefault<GameEntity>();
            if (tempEntity != null)
            {
                this.Platform = tempEntity.GetScriptComponents<SynchedMissionObject>().SingleOrDefault<SynchedMissionObject>();
                editorEntitySet = true;
            } else
            {
                editorEntitySet = false;
            }

            if (this.Platform == null)
            {
                return;
            }

            if (EnabledInEditor == true && editorEntitySet == true)
            {
                this.Move(dt);
            }
        }

    }
}

C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TaleWorlds.Engine;
using TaleWorlds.Library;
using TaleWorlds.MountAndBlade;
using TaleWorlds.Core;
using NetworkMessages.FromServer;
using TaleWorlds.Diamond.ChatSystem.Library;

namespace MyMods
{
    public class CS_DamageZone : ScriptComponentBehavior
    {

        public bool EnabledInGame = false;
        public int InflictedDamage = 0;
        public float DamageTimeInterval = 1.000f;
        protected float Max_x = 0.0f;
        protected float Min_x = 0.0f;
        protected float Max_y = 0.0f;
        protected float Min_y = 0.0f;
        protected float Max_z = 0.0f;
        protected float Min_z = 0.0f;
        protected float timeInZone = 0.0f;


        private void CheckIfInDamageZone(float frameTime)
        {

            Vec3 agentPosition;
            Blow blow = new Blow();

            timeInZone += frameTime;

            if (timeInZone < DamageTimeInterval)
            {
                return;
            }
            else
            {
                timeInZone = 0f;
            }

            foreach (Agent gameAgent in Mission.Current.Agents.ToList())
            {
                agentPosition = gameAgent.GetChestGlobalPosition();


                if (agentPosition.x > Min_x && agentPosition.x < Max_x && agentPosition.y > Min_y && agentPosition.y < Max_y && agentPosition.z > Min_z && agentPosition.z < Max_z)
                {

                    blow = new Blow(gameAgent.Index);
                    blow.DamageType = DamageTypes.Blunt;
                    blow.BoneIndex = gameAgent.Monster.HeadLookDirectionBoneIndex;
                    blow.Position = gameAgent.Position;
                    blow.Position.z = blow.Position.z + gameAgent.GetEyeGlobalHeight();
                    blow.WeaponRecord.FillAsMeleeBlow(null, null, -1, -1);
                    blow.InflictedDamage = InflictedDamage;
                    blow.SwingDirection = gameAgent.LookDirection;
                    MatrixFrame frame = gameAgent.Frame;
                    blow.SwingDirection = frame.rotation.TransformToParent(new Vec3(-1f, 0f, 0f, -1f));
                    blow.SwingDirection.Normalize();
                    blow.Direction = blow.SwingDirection;
                    blow.DamageCalculated = true;
                    sbyte mainHandItemBoneIndex = gameAgent.Monster.MainHandItemBoneIndex;
                    AttackCollisionData attackCollisionDataForDebugPurpose = AttackCollisionData.GetAttackCollisionDataForDebugPurpose(false, false, false, true, false, false, false, false, false, false, false, false, CombatCollisionResult.StrikeAgent, -1, 0, 2, blow.BoneIndex, BoneBodyPartType.Head, mainHandItemBoneIndex, Agent.UsageDirection.AttackLeft, -1, CombatHitResultFlags.NormalHit, 0.5f, 1f, 0f, 0f, 0f, 0f, 0f, 0f, Vec3.Up, blow.Direction, blow.Position, Vec3.Zero, Vec3.Zero, gameAgent.Velocity, Vec3.Up);
                    gameAgent.RegisterBlow(blow, attackCollisionDataForDebugPurpose);

                }
            }
        }

        protected override void OnInit()
        {
            base.OnInit();
            base.SetScriptComponentToTick(this.GetTickRequirement());

            List<GameEntity> entityList = base.GameEntity.GetEntityAndChildren().ToList();
            Max_x = entityList.Max(a => a.GlobalPosition.x);
            Min_x = entityList.Min(a => a.GlobalPosition.x);
            Max_y = entityList.Max(a => a.GlobalPosition.y);
            Min_y = entityList.Min(a => a.GlobalPosition.y);
            Max_z = entityList.Max(a => a.GlobalPosition.z);
            Min_z = entityList.Min(a => a.GlobalPosition.z);

        }

        public override ScriptComponentBehavior.TickRequirement GetTickRequirement()
        {
            return ScriptComponentBehavior.TickRequirement.Tick | base.GetTickRequirement();
        }

        protected override void OnTick(float dt)
        {
            if (EnabledInGame == true)
            {
                if(GameNetwork.IsMultiplayer)
                {
                    if(GameNetwork.IsServer)
                    {
                        this.CheckIfInDamageZone(dt);
                    }
                }
                else
                {
                    this.CheckIfInDamageZone(dt);
                }
            }
        }

        protected override void OnEditorTick(float dt)
        {
            base.OnEditorTick(dt);
        }

    }
}

C#:
using NetworkMessages.FromServer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TaleWorlds.Core;
using TaleWorlds.Engine;
using TaleWorlds.Library;
using TaleWorlds.MountAndBlade;

namespace MyMods
{
    public class CS_Teleport : ScriptComponentBehavior
    {
        public bool EnabledInGame = false;
        public string TeleportTag = "cs_teleport_<Number>";
        protected float Max_x = 0.0f;
        protected float Min_x = 0.0f;
        protected float Max_y = 0.0f;
        protected float Min_y = 0.0f;
        protected float Max_z = 0.0f;
        protected float Min_z = 0.0f;
        protected GameEntity TeleportPoint = null;
        protected Vec3 TeleportPointVec;

        private void CheckIfAgentInTeleport(float dt)
        {
            Vec3 agentPosition;

            foreach (Agent gameAgent in Mission.Current.Agents.ToList())
            {
                agentPosition = gameAgent.GetChestGlobalPosition();

                if (agentPosition.x > Min_x && agentPosition.x < Max_x && agentPosition.y > Min_y && agentPosition.y < Max_y && agentPosition.z > Min_z && agentPosition.z < Max_z)
                {
                    gameAgent.TeleportToPosition(TeleportPointVec);
                }
            }
        }

        protected override void OnInit()
        {
            base.OnInit();
            base.SetScriptComponentToTick(this.GetTickRequirement());

            List<GameEntity> entityList = base.GameEntity.GetEntityAndChildren().ToList();
            Max_x = entityList.Max(a => a.GlobalPosition.x);
            Min_x = entityList.Min(a => a.GlobalPosition.x);
            Max_y = entityList.Max(a => a.GlobalPosition.y);
            Min_y = entityList.Min(a => a.GlobalPosition.y);
            Max_z = entityList.Max(a => a.GlobalPosition.z);
            Min_z = entityList.Min(a => a.GlobalPosition.z);

            TeleportPoint = Mission.Current.Scene.FindEntitiesWithTag(TeleportTag).ToList<GameEntity>().FirstOrDefault();
            TeleportPointVec = TeleportPoint.GlobalPosition;
        }

        public override ScriptComponentBehavior.TickRequirement GetTickRequirement()
        {
            return ScriptComponentBehavior.TickRequirement.Tick | base.GetTickRequirement();
        }

        protected override void OnTick(float dt)
        {
            if (EnabledInGame == true)
            {
                if (GameNetwork.IsMultiplayer)
                {
                    if (GameNetwork.IsServer)
                    {
                        this.CheckIfAgentInTeleport(dt);
                    }
                }
                else
                {
                    this.CheckIfAgentInTeleport(dt);
                }
            }
        }

        protected override void OnEditorTick(float dt)
        {
            base.OnEditorTick(dt);
        }
    }
}
 
Last edited:
Top Bottom