Author Topic: Guide: Dedicated serverrs, Suimono 2.1.4 buoyancy and you!  (Read 1522 times)

lonwolf

  • Newbie
  • *
  • Posts: 6
    • View Profile
Guide: Dedicated serverrs, Suimono 2.1.4 buoyancy and you!
« on: October 16, 2017, 05:08:59 AM »
Hey there!

After a few tries (and with some hints) I managed to change up the buoyancy system so that it can also work on a dedicated server (or local authorithy client) to add forces (no matter activation range, camera frustum and raycasts). This is a work in progress that can still possibly be improved but I wanted to share these with you.

Opening remarks
- Running dedicated server instance with -batchmode and -nographics. Not needed and it can still work.
- We want to eliminate the need to have single points each run a fx_buoyancy gameobject since we want to decide in a NetworkBehaviour who runs it and who doesn't with less callbacks and less game objects

Changes
- in cameraTools.cs make sure you disable rendering on -nographics instances in Start() and all other callbacks (OnPreRender, OnPostRender, Update, LateUpdate)
Code: [Select]
if (m_bRenderBlocked) //where m_bRenderBlocked = Args.Provided("-nographics") or something like this, i use Msf.Args from master server framework asset
            {
                return;
            }

- Implement a custom buoyancy script, I attached my own example in CustomBuoyancy.cs
- Modify Suimono Module GetHeightAll function and CalculateHeights to accept more parameters
Code: [Select]
public float[] SuimonoGetHeightAll(Vector3 _testObject, bool _bComputeHeightsRaycast = true, Suimono.Core.SuimonoObject _suimonoSurface = null, Transform _suimonoSurfaceObject = null)
        {
            // Get Heights
            CalculateHeights(_testObject, _bComputeHeightsRaycast, _suimonoSurface, _suimonoSurfaceObject);

- Modify CalculateHeights to do a raycast only if you don't supply the surface already
Code: [Select]
void CalculateHeights(Vector3 _testObject, bool _bComputeHeightsRaycast = true, Suimono.Core.SuimonoObject _suimonoSurface = null, Transform _suimonoSurfaceObject = null)
        {
            getheight = -1.0f;
            getheightC = -1.0f;
            getheightT = -1.0f;
            getheightR = 0.0f;
            isOverWater = false;
            surfaceLevel = -1.0f;

            bool bShouldContinueComputing = false;
            if (_bComputeHeightsRaycast)
            {
                layermask = 1 << layerWaterNum;
                testpos = new Vector3(_testObject.x, _testObject.y + 5000f, _testObject.z);

                if (Physics.Raycast(testpos, -Vector3.up, out hit, 10000f, layermask))
                {
                    targetSurface = hit.transform.gameObject;
                    if (currentSurfaceObject != targetSurface || suimonoObject == null)
                    {
                        currentSurfaceObject = targetSurface;
                        if (hit.transform.parent != null)
                        {
                            suimonoObject = hit.transform.parent.gameObject.GetComponent<Suimono.Core.SuimonoObject>();
                        }
                    }

                    if (suimonoObject != null && enableInteraction && suimonoObject.enableInteraction)
                    {
                        if (suimonoObject.typeIndex == 0)
                        {
                            heightObject = hit.transform;
                        }
                        else
                        {
                            heightObject = hit.transform.parent;
                        }
                        bShouldContinueComputing = hit.collider != null;
                    }
                }
            }
            else
            {
                targetSurface = _suimonoSurfaceObject.gameObject;
                currentSurfaceObject = targetSurface;
                suimonoObject = _suimonoSurface;

                if (suimonoObject != null && suimonoObject.typeIndex == 0 && enableInteraction && suimonoObject.enableInteraction)
                {
                    //only for infinite ocean we support custom buoyancy
                    heightObject = _suimonoSurfaceObject;
                    bShouldContinueComputing = true;
                }
            }
           

            if (bShouldContinueComputing)
            {
                isOverWater = true;
                surfaceLevel = heightObject.position.y;

                //calculate relative position
                baseHeight = heightObject.position.y;
                baseAngle = heightObject.rotation.y;
                relativePos.x = ((heightObject.position.x - _testObject.x) / (20.0f * heightObject.localScale.x) + 1f) * 0.5f * heightObject.localScale.x;
                relativePos.y = ((heightObject.position.z - _testObject.z) / (20.0f * heightObject.localScale.z) + 1f) * 0.5f * heightObject.localScale.z;

                //calculate offset
                useLocalTime = suimonoObject.localTime;
                flow_dirC = SuimonoConvertAngleToVector(suimonoObject.flowDirection);
                flowSpeed0 = new Vector2(flow_dirC.x * useLocalTime, flow_dirC.y * useLocalTime);
                flowSpeed1 = new Vector2(flow_dirC.x * useLocalTime * 0.25f, flow_dirC.y * useLocalTime * 0.25f);
                flowSpeed2 = new Vector2(flow_dirC.x * useLocalTime * 0.0625f, flow_dirC.y * useLocalTime * 0.0625f);
                flowSpeed3 = new Vector2(flow_dirC.x * useLocalTime * 0.125f, flow_dirC.y * useLocalTime * 0.125f);
                tScale = (1.0f / (suimonoObject.waveScale));
                oPos = new Vector2(0.0f - suimonoObject.savePos.x, 0.0f - suimonoObject.savePos.y);

                //calculate texture coordinates
                if (heightTex != null)
                {

                    texCoord.x = (relativePos.x * tScale + flowSpeed0.x + (oPos.x)) * heightTex.width;
                    texCoord.z = (relativePos.y * tScale + flowSpeed0.y + (oPos.y)) * heightTex.height;
                    texCoord1.x = ((relativePos.x * tScale * 0.75f) - flowSpeed1.x + (oPos.x * 0.75f)) * heightTex.width;
                    texCoord1.z = ((relativePos.y * tScale * 0.75f) - flowSpeed1.y + (oPos.y * 0.75f)) * heightTex.height;

                    texCoordT.x = (relativePos.x * tScale + flowSpeed0.x + (oPos.x)) * heightTexT.width;
                    texCoordT.z = (relativePos.y * tScale + flowSpeed0.y + (oPos.y)) * heightTexT.height;
                    texCoordT1.x = ((relativePos.x * tScale * 0.5f) - flowSpeed1.x + (oPos.x * 0.5f)) * heightTexT.width;
                    texCoordT1.z = ((relativePos.y * tScale * 0.5f) - flowSpeed1.y + (oPos.y * 0.5f)) * heightTexT.height;

                    texCoordR.x = (relativePos.x * suimonoObject.lgWaveScale * tScale + flowSpeed2.x + (oPos.x * suimonoObject.lgWaveScale)) * heightTexR.width;
                    texCoordR.z = (relativePos.y * suimonoObject.lgWaveScale * tScale + flowSpeed2.y + (oPos.y * suimonoObject.lgWaveScale)) * heightTexR.height;
                    texCoordR1.x = ((relativePos.x * suimonoObject.lgWaveScale * tScale) + flowSpeed3.x + (oPos.x * suimonoObject.lgWaveScale)) * heightTexR.width;
                    texCoordR1.z = ((relativePos.y * suimonoObject.lgWaveScale * tScale) + flowSpeed3.y + (oPos.y * suimonoObject.lgWaveScale)) * heightTexR.height;

                    //rotate coordinates
                    if (baseAngle != 0.0f)
                    {

                        pivotPoint = new Vector3(heightTex.width * heightObject.localScale.x * tScale * 0.5f + (flowSpeed0.x * heightTex.width), 0f, heightTex.height * heightObject.localScale.z * tScale * 0.5f + (flowSpeed0.y * heightTex.height));
                        texCoord = RotatePointAroundPivot(texCoord, pivotPoint, heightObject.eulerAngles);
                        pivotPoint = new Vector3(heightTex.width * heightObject.localScale.x * tScale * 0.5f * 0.75f - (flowSpeed1.x * heightTex.width), 0f, heightTex.height * heightObject.localScale.z * tScale * 0.5f * 0.75f - (flowSpeed1.y * heightTex.height));
                        texCoord1 = RotatePointAroundPivot(texCoord1, pivotPoint, heightObject.eulerAngles);

                        pivotPoint = new Vector3(heightTexT.width * heightObject.localScale.x * tScale * 0.5f + (flowSpeed0.x * heightTexT.width), 0f, heightTexT.height * heightObject.localScale.z * tScale * 0.5f + (flowSpeed0.y * heightTexT.height));
                        texCoordT = RotatePointAroundPivot(texCoordT, pivotPoint, heightObject.eulerAngles);
                        pivotPoint = new Vector3(heightTexT.width * heightObject.localScale.x * tScale * 0.5f * 0.5f - (flowSpeed1.x * heightTexT.width), 0f, heightTexT.height * heightObject.localScale.z * tScale * 0.5f * 0.5f - (flowSpeed1.y * heightTexT.height));
                        texCoordT1 = RotatePointAroundPivot(texCoordT1, pivotPoint, heightObject.eulerAngles);

                        pivotPoint = new Vector3(heightTexR.width * heightObject.localScale.x * suimonoObject.lgWaveScale * tScale * 0.5f + (flowSpeed2.x * heightTexR.width), 0f, heightTexR.height * heightObject.localScale.z * suimonoObject.lgWaveScale * tScale * 0.5f + (flowSpeed2.y * heightTexR.height));
                        texCoordR = RotatePointAroundPivot(texCoordR, pivotPoint, heightObject.eulerAngles);
                        pivotPoint = new Vector3(heightTexR.width * heightObject.localScale.x * suimonoObject.lgWaveScale * tScale * 0.5f + (flowSpeed3.x * heightTexR.width), 0f, heightTexR.height * heightObject.localScale.z * suimonoObject.lgWaveScale * tScale * 0.5f + (flowSpeed3.y * heightTexR.height));
                        texCoordR1 = RotatePointAroundPivot(texCoordR1, pivotPoint, heightObject.eulerAngles);
                    }

                    //decode height value
                    heightVal0 = DecodeHeightPixels(texCoord.x, texCoord.z, 0);
                    heightVal1 = DecodeHeightPixels(texCoord1.x, texCoord1.z, 0);
                    heightValT0 = DecodeHeightPixels(texCoordT.x, texCoordT.z, 1);
                    heightValT1 = DecodeHeightPixels(texCoordT1.x, texCoordT1.z, 1);
                    heightValR0 = DecodeHeightPixels(texCoordR.x, texCoordR.z, 2);
                    heightValR1 = DecodeHeightPixels(texCoordR1.x, texCoordR1.z, 2);

                    //set heightvalue
                    getheightC = (heightVal0.a + heightVal1.a) * 0.8f;
                    getheightT = ((heightValT0.a * 0.2f) + (heightValT0.a * heightValT1.a * 0.8f)) * suimonoObject.turbulenceFactor * 0.5f;
                    getheightR = ((heightValR0.a * 4.0f) + (heightValR1.a * 3.0f));

                    getheight = baseHeight + (getheightC * suimonoObject.waveHeight);
                    getheight += (getheightT * suimonoObject.waveHeight);
                    getheight += (getheightR * suimonoObject.lgWaveHeight);
                    getheight = Mathf.Lerp(baseHeight, getheight, suimonoObject.useHeightProjection);
                }
            }
        }

- How to use: in your network behaviour add some GameObject transforms to use as buoyancy points and some strengths (add more parameters as needed)
Code: [Select]
[Header("Buoyancy")]
    public Transform[] m_BuoyancyTransforms;
    public float[] m_BuoyancyStrengths;
    private CustomBuoyancy m_CustomBuoyancy = new CustomBuoyancy();

- On your callbacks (server or client) make sure you init the buoyancy
Code: [Select]
if(isServer)
{
    m_CustomBuoyancy.Init(m_SuimonoModule, m_SuimonoSurface, m_SuimonoSurfaceObject, m_BuoyancyTransforms, m_BuoyancyStrengths, transform, m_BoatBody);
}

- Route the FixedUpdate call to a callback on your network behaviour
Code: [Select]
private void FixedUpdate()
    {
         m_CustomBuoyancy.FixedUpdate();
     }

And that's it! you now will have the forces calculated on server for instance on these objects! As long as you don't call Init() the FixedUpdate() will do nothing.
Script can be further optimized by customization:
One example would be for server objects - scan in a range of 300 - no players found? - make buoyancy add a force to simply put the object to surfaceLevel instead of calculating wave positions, or even simpler, disable gravity and set position to surfaceLevel and stuff like that.

Without this script and using the default fx_buoyancy object with no modification you can get into a desyncronization or rigidbodies ( a boat can be flipped upside down on a client and it can be good in other machines). Using this approach I have managed to have everything working correctly. Next steps taken were to sync Suimono system time and use Tenkoku EncodeData and DecodeData to sync it's own settings on client connect.