Merge branch 'master' into careminster
Conflicts: OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs OpenSim/Region/Framework/Scenes/Scene.cs OpenSim/Region/Framework/Scenes/ScenePresence.cs bin/OpenSimDefaults.iniavinationmerge
commit
7914eaa357
|
@ -53,7 +53,7 @@ namespace OpenSim.Groups
|
||||||
private ForeignImporter m_ForeignImporter;
|
private ForeignImporter m_ForeignImporter;
|
||||||
|
|
||||||
private Dictionary<string, bool> m_ActiveRequests = new Dictionary<string, bool>();
|
private Dictionary<string, bool> m_ActiveRequests = new Dictionary<string, bool>();
|
||||||
private const int GROUPS_CACHE_TIMEOUT = 5 * 60; // 5 minutes
|
private const int GROUPS_CACHE_TIMEOUT = 1 * 60; // 1 minutes
|
||||||
|
|
||||||
// This all important cache cahces objects of different types:
|
// This all important cache cahces objects of different types:
|
||||||
// group-<GroupID> or group-<Name> => ExtendedGroupRecord
|
// group-<GroupID> or group-<Name> => ExtendedGroupRecord
|
||||||
|
|
|
@ -292,7 +292,7 @@ namespace OpenSim.Framework
|
||||||
public Vector3 AtAxis;
|
public Vector3 AtAxis;
|
||||||
public Vector3 LeftAxis;
|
public Vector3 LeftAxis;
|
||||||
public Vector3 UpAxis;
|
public Vector3 UpAxis;
|
||||||
public bool ChangedGrid;
|
public bool SenderWantsToWaitForRoot;
|
||||||
|
|
||||||
public float Far;
|
public float Far;
|
||||||
public float Aspect;
|
public float Aspect;
|
||||||
|
@ -363,8 +363,9 @@ namespace OpenSim.Framework
|
||||||
args["left_axis"] = OSD.FromString(LeftAxis.ToString());
|
args["left_axis"] = OSD.FromString(LeftAxis.ToString());
|
||||||
args["up_axis"] = OSD.FromString(UpAxis.ToString());
|
args["up_axis"] = OSD.FromString(UpAxis.ToString());
|
||||||
|
|
||||||
|
//backwards compatibility
|
||||||
args["changed_grid"] = OSD.FromBoolean(ChangedGrid);
|
args["changed_grid"] = OSD.FromBoolean(SenderWantsToWaitForRoot);
|
||||||
|
args["wait_for_root"] = OSD.FromBoolean(SenderWantsToWaitForRoot);
|
||||||
args["far"] = OSD.FromReal(Far);
|
args["far"] = OSD.FromReal(Far);
|
||||||
args["aspect"] = OSD.FromReal(Aspect);
|
args["aspect"] = OSD.FromReal(Aspect);
|
||||||
|
|
||||||
|
@ -537,8 +538,8 @@ namespace OpenSim.Framework
|
||||||
if (args["up_axis"] != null)
|
if (args["up_axis"] != null)
|
||||||
Vector3.TryParse(args["up_axis"].AsString(), out AtAxis);
|
Vector3.TryParse(args["up_axis"].AsString(), out AtAxis);
|
||||||
|
|
||||||
if (args["changed_grid"] != null)
|
if (args.ContainsKey("wait_for_root") && args["wait_for_root"] != null)
|
||||||
ChangedGrid = args["changed_grid"].AsBoolean();
|
SenderWantsToWaitForRoot = args["wait_for_root"].AsBoolean();
|
||||||
|
|
||||||
if (args["far"] != null)
|
if (args["far"] != null)
|
||||||
Far = (float)(args["far"].AsReal());
|
Far = (float)(args["far"].AsReal());
|
||||||
|
|
|
@ -1302,6 +1302,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
queue.Enqueue(buffer);
|
queue.Enqueue(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (packet.Type == PacketType.CompleteAgentMovement)
|
||||||
|
{
|
||||||
|
// Send ack straight away to let the viewer know that we got it.
|
||||||
|
SendAckImmediate(endPoint, packet.Header.Sequence);
|
||||||
|
|
||||||
|
// We need to copy the endpoint so that it doesn't get changed when another thread reuses the
|
||||||
|
// buffer.
|
||||||
|
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
|
||||||
|
|
||||||
|
Util.FireAndForget(HandleCompleteMovementIntoRegion, array);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine which agent this packet came from
|
// Determine which agent this packet came from
|
||||||
|
@ -1676,6 +1689,72 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleCompleteMovementIntoRegion(object o)
|
||||||
|
{
|
||||||
|
IPEndPoint endPoint = null;
|
||||||
|
IClientAPI client = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
object[] array = (object[])o;
|
||||||
|
endPoint = (IPEndPoint)array[0];
|
||||||
|
CompleteAgentMovementPacket packet = (CompleteAgentMovementPacket)array[1];
|
||||||
|
|
||||||
|
// Determine which agent this packet came from
|
||||||
|
int count = 20;
|
||||||
|
bool ready = false;
|
||||||
|
while (!ready && count-- > 0)
|
||||||
|
{
|
||||||
|
if (m_scene.TryGetClient(endPoint, out client) && client.IsActive && client.SceneAgent != null)
|
||||||
|
{
|
||||||
|
LLClientView llClientView = (LLClientView)client;
|
||||||
|
LLUDPClient udpClient = llClientView.UDPClient;
|
||||||
|
if (udpClient != null && udpClient.IsConnected)
|
||||||
|
ready = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.Debug("[LLUDPSERVER]: Received a CompleteMovementIntoRegion in " + m_scene.RegionInfo.RegionName + " (not ready yet)");
|
||||||
|
Thread.Sleep(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.Debug("[LLUDPSERVER]: Received a CompleteMovementIntoRegion in " + m_scene.RegionInfo.RegionName + " (not ready yet)");
|
||||||
|
Thread.Sleep(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IncomingPacket incomingPacket1;
|
||||||
|
|
||||||
|
// Inbox insertion
|
||||||
|
if (UsePools)
|
||||||
|
{
|
||||||
|
incomingPacket1 = m_incomingPacketPool.GetObject();
|
||||||
|
incomingPacket1.Client = (LLClientView)client;
|
||||||
|
incomingPacket1.Packet = packet;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
incomingPacket1 = new IncomingPacket((LLClientView)client, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
packetInbox.Enqueue(incomingPacket1);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat(
|
||||||
|
"[LLUDPSERVER]: CompleteMovementIntoRegion handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}",
|
||||||
|
endPoint != null ? endPoint.ToString() : "n/a",
|
||||||
|
client != null ? client.Name : "unknown",
|
||||||
|
client != null ? client.AgentId.ToString() : "unknown",
|
||||||
|
e.Message,
|
||||||
|
e.StackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Send an ack immediately to the given endpoint.
|
/// Send an ack immediately to the given endpoint.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -689,6 +689,20 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath();
|
agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version.Equals("SIMULATION/0.2"))
|
||||||
|
TransferAgent_V2(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, version, out reason);
|
||||||
|
else
|
||||||
|
TransferAgent_V1(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, version, out reason);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TransferAgent_V1(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination,
|
||||||
|
IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, string version, out string reason)
|
||||||
|
{
|
||||||
|
ulong destinationHandle = finalDestination.RegionHandle;
|
||||||
|
AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
|
||||||
|
|
||||||
|
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Using TP V1");
|
||||||
// Let's create an agent there if one doesn't exist yet.
|
// Let's create an agent there if one doesn't exist yet.
|
||||||
// NOTE: logout will always be false for a non-HG teleport.
|
// NOTE: logout will always be false for a non-HG teleport.
|
||||||
bool logout = false;
|
bool logout = false;
|
||||||
|
@ -710,7 +724,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
m_interRegionTeleportCancels.Value++;
|
m_interRegionTeleportCancels.Value++;
|
||||||
|
|
||||||
m_log.DebugFormat(
|
m_log.DebugFormat(
|
||||||
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request",
|
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request",
|
||||||
sp.Name, finalDestination.RegionName, sp.Scene.Name);
|
sp.Name, finalDestination.RegionName, sp.Scene.Name);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -732,11 +746,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
// OK, it got this agent. Let's close some child agents
|
// OK, it got this agent. Let's close some child agents
|
||||||
sp.CloseChildAgents(newRegionX, newRegionY);
|
sp.CloseChildAgents(newRegionX, newRegionY);
|
||||||
|
|
||||||
IClientIPEndpoint ipepClient;
|
IClientIPEndpoint ipepClient;
|
||||||
|
string capsPath = String.Empty;
|
||||||
if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY))
|
if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY))
|
||||||
{
|
{
|
||||||
m_log.DebugFormat(
|
m_log.DebugFormat(
|
||||||
"[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for incoming agent {3} from {4}",
|
"[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for incoming agent {3} from {4}",
|
||||||
finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name);
|
finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name);
|
||||||
|
|
||||||
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent...");
|
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent...");
|
||||||
|
@ -783,10 +798,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
// Let's send a full update of the agent. This is a synchronous call.
|
// Let's send a full update of the agent. This is a synchronous call.
|
||||||
AgentData agent = new AgentData();
|
AgentData agent = new AgentData();
|
||||||
sp.CopyTo(agent);
|
sp.CopyTo(agent);
|
||||||
agent.Position = position;
|
agent.Position = agentCircuit.startpos;
|
||||||
SetCallbackURL(agent, sp.Scene.RegionInfo);
|
SetCallbackURL(agent, sp.Scene.RegionInfo);
|
||||||
|
|
||||||
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Updating agent...");
|
|
||||||
|
|
||||||
// We will check for an abort before UpdateAgent since UpdateAgent will require an active viewer to
|
// We will check for an abort before UpdateAgent since UpdateAgent will require an active viewer to
|
||||||
// establish th econnection to the destination which makes it return true.
|
// establish th econnection to the destination which makes it return true.
|
||||||
|
@ -821,7 +835,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
m_log.WarnFormat(
|
m_log.WarnFormat(
|
||||||
"[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Keeping avatar in source region.",
|
"[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Keeping avatar in source region.",
|
||||||
sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
|
sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
|
||||||
|
|
||||||
Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established.");
|
Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -831,7 +845,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
m_interRegionTeleportCancels.Value++;
|
m_interRegionTeleportCancels.Value++;
|
||||||
|
|
||||||
m_log.DebugFormat(
|
m_log.DebugFormat(
|
||||||
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request",
|
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request",
|
||||||
sp.Name, finalDestination.RegionName, sp.Scene.Name);
|
sp.Name, finalDestination.RegionName, sp.Scene.Name);
|
||||||
|
|
||||||
CleanupFailedInterRegionTeleport(sp, currentAgentCircuit.SessionID.ToString(), finalDestination);
|
CleanupFailedInterRegionTeleport(sp, currentAgentCircuit.SessionID.ToString(), finalDestination);
|
||||||
|
@ -848,6 +862,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
// closes our existing agent which is still signalled as root.
|
// closes our existing agent which is still signalled as root.
|
||||||
sp.IsChildAgent = true;
|
sp.IsChildAgent = true;
|
||||||
|
|
||||||
|
// OK, send TPFinish to the client, so that it starts the process of contacting the destination region
|
||||||
if (m_eqModule != null)
|
if (m_eqModule != null)
|
||||||
{
|
{
|
||||||
m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID);
|
m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID);
|
||||||
|
@ -877,7 +892,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
m_log.WarnFormat(
|
m_log.WarnFormat(
|
||||||
"[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.",
|
"[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.",
|
||||||
sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
|
sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
|
||||||
|
|
||||||
Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Destination region did not signal teleport completion.");
|
Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Destination region did not signal teleport completion.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -906,13 +921,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
|
|
||||||
if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg))
|
if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg))
|
||||||
{
|
{
|
||||||
// We need to delay here because Imprudence viewers, unlike v1 or v3, have a short (<200ms, <500ms) delay before
|
// RED ALERT!!!!
|
||||||
// they regard the new region as the current region after receiving the AgentMovementComplete
|
// PLEASE DO NOT DECREASE THIS WAIT TIME UNDER ANY CIRCUMSTANCES.
|
||||||
// response. If close is sent before then, it will cause the viewer to quit instead.
|
// THE VIEWERS SEEM TO NEED SOME TIME AFTER RECEIVING MoveAgentIntoRegion
|
||||||
//
|
// BEFORE THEY SETTLE IN THE NEW REGION.
|
||||||
// This sleep can be increased if necessary. However, whilst it's active,
|
// DECREASING THE WAIT TIME HERE WILL EITHER RESULT IN A VIEWER CRASH OR
|
||||||
// an agent cannot teleport back to this region if it has teleported away.
|
// IN THE AVIE BEING PLACED IN INFINITY FOR A COUPLE OF SECONDS.
|
||||||
Thread.Sleep(3000);
|
Thread.Sleep(5000);
|
||||||
|
|
||||||
sp.Scene.IncomingCloseAgent(sp.UUID, false);
|
sp.Scene.IncomingCloseAgent(sp.UUID, false);
|
||||||
}
|
}
|
||||||
|
@ -923,6 +938,134 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TransferAgent_V2(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination,
|
||||||
|
IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, string version, out string reason)
|
||||||
|
{
|
||||||
|
ulong destinationHandle = finalDestination.RegionHandle;
|
||||||
|
AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
|
||||||
|
|
||||||
|
// Let's create an agent there if one doesn't exist yet.
|
||||||
|
// NOTE: logout will always be false for a non-HG teleport.
|
||||||
|
bool logout = false;
|
||||||
|
if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout))
|
||||||
|
{
|
||||||
|
m_interRegionTeleportFailures.Value++;
|
||||||
|
|
||||||
|
sp.ControllingClient.SendTeleportFailed(String.Format("Teleport refused: {0}", reason));
|
||||||
|
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}",
|
||||||
|
sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Past this point we have to attempt clean up if the teleport fails, so update transfer state.
|
||||||
|
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring);
|
||||||
|
|
||||||
|
IClientIPEndpoint ipepClient;
|
||||||
|
string capsPath = String.Empty;
|
||||||
|
if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY))
|
||||||
|
{
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for agent {3} from {4}",
|
||||||
|
finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name);
|
||||||
|
|
||||||
|
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent...");
|
||||||
|
#region IP Translation for NAT
|
||||||
|
// Uses ipepClient above
|
||||||
|
if (sp.ClientView.TryGet(out ipepClient))
|
||||||
|
{
|
||||||
|
endPoint.Address = NetworkUtil.GetIPFor(ipepClient.EndPoint, endPoint.Address);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
agentCircuit.CapsPath = sp.Scene.CapsModule.GetChildSeed(sp.UUID, reg.RegionHandle);
|
||||||
|
capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to set this here to avoid an unlikely race condition when teleporting to a neighbour simulator,
|
||||||
|
// where that neighbour simulator could otherwise request a child agent create on the source which then
|
||||||
|
// closes our existing agent which is still signalled as root.
|
||||||
|
//sp.IsChildAgent = true;
|
||||||
|
|
||||||
|
// New protocol: send TP Finish directly, without prior ES or EAC. That's what happens in the Linden grid
|
||||||
|
if (m_eqModule != null)
|
||||||
|
m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID);
|
||||||
|
else
|
||||||
|
sp.ControllingClient.SendRegionTeleport(destinationHandle, 13, endPoint, 4,
|
||||||
|
teleportFlags, capsPath);
|
||||||
|
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}",
|
||||||
|
capsPath, sp.Scene.RegionInfo.RegionName, sp.Name);
|
||||||
|
|
||||||
|
// Let's send a full update of the agent.
|
||||||
|
AgentData agent = new AgentData();
|
||||||
|
sp.CopyTo(agent);
|
||||||
|
agent.Position = agentCircuit.startpos;
|
||||||
|
agent.SenderWantsToWaitForRoot = true;
|
||||||
|
//SetCallbackURL(agent, sp.Scene.RegionInfo);
|
||||||
|
|
||||||
|
// Send the Update. If this returns true, we know the client has contacted the destination
|
||||||
|
// via CompleteMovementIntoRegion, so we can let go.
|
||||||
|
// If it returns false, something went wrong, and we need to abort.
|
||||||
|
if (!UpdateAgent(reg, finalDestination, agent, sp))
|
||||||
|
{
|
||||||
|
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
|
||||||
|
{
|
||||||
|
m_interRegionTeleportAborts.Value++;
|
||||||
|
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.",
|
||||||
|
sp.Name, finalDestination.RegionName, sp.Scene.Name);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_log.WarnFormat(
|
||||||
|
"[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Keeping avatar in source region.",
|
||||||
|
sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
|
||||||
|
|
||||||
|
Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
|
||||||
|
|
||||||
|
// May need to logout or other cleanup
|
||||||
|
AgentHasMovedAway(sp, logout);
|
||||||
|
|
||||||
|
// Well, this is it. The agent is over there.
|
||||||
|
KillEntity(sp.Scene, sp.LocalId);
|
||||||
|
|
||||||
|
// Now let's make it officially a child agent
|
||||||
|
sp.MakeChildAgent();
|
||||||
|
|
||||||
|
// OK, it got this agent. Let's close some child agents
|
||||||
|
sp.CloseChildAgents(newRegionX, newRegionY);
|
||||||
|
|
||||||
|
// Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone
|
||||||
|
|
||||||
|
if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg))
|
||||||
|
{
|
||||||
|
// RED ALERT!!!!
|
||||||
|
// PLEASE DO NOT DECREASE THIS WAIT TIME UNDER ANY CIRCUMSTANCES.
|
||||||
|
// THE VIEWERS SEEM TO NEED SOME TIME AFTER RECEIVING MoveAgentIntoRegion
|
||||||
|
// BEFORE THEY SETTLE IN THE NEW REGION.
|
||||||
|
// DECREASING THE WAIT TIME HERE WILL EITHER RESULT IN A VIEWER CRASH OR
|
||||||
|
// IN THE AVIE BEING PLACED IN INFINITY FOR A COUPLE OF SECONDS.
|
||||||
|
Thread.Sleep(5000);
|
||||||
|
sp.Scene.IncomingCloseAgent(sp.UUID, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// now we have a child agent in this region.
|
||||||
|
sp.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation.
|
/// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -936,11 +1079,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
{
|
{
|
||||||
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
|
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
|
||||||
|
|
||||||
sp.IsChildAgent = false;
|
if (sp.IsChildAgent) // We had set it to child before attempted TP (V1)
|
||||||
ReInstantiateScripts(sp);
|
{
|
||||||
|
sp.IsChildAgent = false;
|
||||||
EnableChildAgents(sp);
|
ReInstantiateScripts(sp);
|
||||||
|
|
||||||
|
EnableChildAgents(sp);
|
||||||
|
}
|
||||||
// Finally, kill the agent we just created at the destination.
|
// Finally, kill the agent we just created at the destination.
|
||||||
// XXX: Possibly this should be done asynchronously.
|
// XXX: Possibly this should be done asynchronously.
|
||||||
Scene.SimulationService.CloseAgent(finalDestination, sp.UUID, auth_token);
|
Scene.SimulationService.CloseAgent(finalDestination, sp.UUID, auth_token);
|
||||||
|
|
|
@ -79,7 +79,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence
|
||||||
|
|
||||||
public void OnConnectionClose(IClientAPI client)
|
public void OnConnectionClose(IClientAPI client)
|
||||||
{
|
{
|
||||||
if (!client.SceneAgent.IsChildAgent)
|
if (client != null && client.SceneAgent != null && !client.SceneAgent.IsChildAgent)
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("[PRESENCE DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName);
|
// m_log.DebugFormat("[PRESENCE DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName);
|
||||||
m_PresenceService.LogoutAgent(client.SessionId);
|
m_PresenceService.LogoutAgent(client.SessionId);
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of this service
|
/// Version of this service
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const string m_Version = "SIMULATION/0.1";
|
private const string m_Version = "SIMULATION/0.2";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Map region ID to scene.
|
/// Map region ID to scene.
|
||||||
|
|
|
@ -230,6 +230,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
public bool m_seeIntoBannedRegion = false;
|
public bool m_seeIntoBannedRegion = false;
|
||||||
public int MaxUndoCount = 5;
|
public int MaxUndoCount = 5;
|
||||||
|
|
||||||
|
public bool SeeIntoRegion { get; set; }
|
||||||
|
|
||||||
// Using this for RegionReady module to prevent LoginsDisabled from changing under our feet;
|
// Using this for RegionReady module to prevent LoginsDisabled from changing under our feet;
|
||||||
public bool LoginLock = false;
|
public bool LoginLock = false;
|
||||||
|
|
||||||
|
@ -861,9 +863,11 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
//Animation states
|
//Animation states
|
||||||
m_useFlySlow = startupConfig.GetBoolean("enableflyslow", false);
|
m_useFlySlow = startupConfig.GetBoolean("enableflyslow", false);
|
||||||
|
|
||||||
|
|
||||||
|
MaxUndoCount = startupConfig.GetInt("MaxPrimUndos", 20);
|
||||||
|
|
||||||
PhysicalPrims = startupConfig.GetBoolean("physical_prim", true);
|
PhysicalPrims = startupConfig.GetBoolean("physical_prim", true);
|
||||||
CollidablePrims = startupConfig.GetBoolean("collidable_prim", true);
|
CollidablePrims = startupConfig.GetBoolean("collidable_prim", true);
|
||||||
|
|
||||||
m_minNonphys = startupConfig.GetFloat("NonPhysicalPrimMin", m_minNonphys);
|
m_minNonphys = startupConfig.GetFloat("NonPhysicalPrimMin", m_minNonphys);
|
||||||
if (RegionInfo.NonphysPrimMin > 0)
|
if (RegionInfo.NonphysPrimMin > 0)
|
||||||
{
|
{
|
||||||
|
@ -3910,15 +3914,10 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Always check estate if this is a login. Always
|
if (!AuthorizeUser(agent, SeeIntoRegion, out reason))
|
||||||
// check if banned regions are to be blacked out.
|
|
||||||
if (vialogin || (!m_seeIntoBannedRegion))
|
|
||||||
{
|
{
|
||||||
if (!AuthorizeUser(agent, out reason))
|
m_authenticateHandler.RemoveCircuit(agent.circuitcode);
|
||||||
{
|
return false;
|
||||||
m_authenticateHandler.RemoveCircuit(agent.circuitcode);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -4158,7 +4157,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
/// <param name="reason">outputs the reason to this string</param>
|
/// <param name="reason">outputs the reason to this string</param>
|
||||||
/// <returns>True if the region accepts this agent. False if it does not. False will
|
/// <returns>True if the region accepts this agent. False if it does not. False will
|
||||||
/// also return a reason.</returns>
|
/// also return a reason.</returns>
|
||||||
protected virtual bool AuthorizeUser(AgentCircuitData agent, out string reason)
|
protected virtual bool AuthorizeUser(AgentCircuitData agent, bool bypassAccessControl, out string reason)
|
||||||
{
|
{
|
||||||
reason = String.Empty;
|
reason = String.Empty;
|
||||||
|
|
||||||
|
@ -4193,52 +4192,59 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
m_log.ErrorFormat("[CONNECTION BEGIN]: Estate Settings is null!");
|
m_log.ErrorFormat("[CONNECTION BEGIN]: Estate Settings is null!");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UUID> agentGroups = new List<UUID>();
|
// We only test the things below when we want to cut off
|
||||||
|
// child agents from being present in the scene for which their root
|
||||||
if (m_groupsModule != null)
|
// agent isn't allowed. Otherwise, we allow child agents. The test for
|
||||||
|
// the root is done elsewhere (QueryAccess)
|
||||||
|
if (!bypassAccessControl)
|
||||||
{
|
{
|
||||||
GroupMembershipData[] GroupMembership = m_groupsModule.GetMembershipData(agent.AgentID);
|
List<UUID> agentGroups = new List<UUID>();
|
||||||
|
|
||||||
if (GroupMembership != null)
|
if (m_groupsModule != null)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < GroupMembership.Length; i++)
|
GroupMembershipData[] GroupMembership = m_groupsModule.GetMembershipData(agent.AgentID);
|
||||||
agentGroups.Add(GroupMembership[i].GroupID);
|
|
||||||
|
if (GroupMembership != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < GroupMembership.Length; i++)
|
||||||
|
agentGroups.Add(GroupMembership[i].GroupID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat("[CONNECTION BEGIN]: GroupMembership is null!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool groupAccess = false;
|
||||||
|
UUID[] estateGroups = RegionInfo.EstateSettings.EstateGroups;
|
||||||
|
|
||||||
|
if (estateGroups != null)
|
||||||
|
{
|
||||||
|
foreach (UUID group in estateGroups)
|
||||||
|
{
|
||||||
|
if (agentGroups.Contains(group))
|
||||||
|
{
|
||||||
|
groupAccess = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_log.ErrorFormat("[CONNECTION BEGIN]: GroupMembership is null!");
|
m_log.ErrorFormat("[CONNECTION BEGIN]: EstateGroups is null!");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool groupAccess = false;
|
if (!RegionInfo.EstateSettings.PublicAccess &&
|
||||||
UUID[] estateGroups = RegionInfo.EstateSettings.EstateGroups;
|
!RegionInfo.EstateSettings.HasAccess(agent.AgentID) &&
|
||||||
|
!groupAccess)
|
||||||
if (estateGroups != null)
|
|
||||||
{
|
|
||||||
foreach (UUID group in estateGroups)
|
|
||||||
{
|
{
|
||||||
if (agentGroups.Contains(group))
|
m_log.WarnFormat("[CONNECTION BEGIN]: Denied access to: {0} ({1} {2}) at {3} because the user does not have access to the estate",
|
||||||
{
|
agent.AgentID, agent.firstname, agent.lastname, RegionInfo.RegionName);
|
||||||
groupAccess = true;
|
reason = String.Format("Denied access to private region {0}: You are not on the access list for that region.",
|
||||||
break;
|
RegionInfo.RegionName);
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
m_log.ErrorFormat("[CONNECTION BEGIN]: EstateGroups is null!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!RegionInfo.EstateSettings.PublicAccess &&
|
|
||||||
!RegionInfo.EstateSettings.HasAccess(agent.AgentID) &&
|
|
||||||
!groupAccess)
|
|
||||||
{
|
|
||||||
m_log.WarnFormat("[CONNECTION BEGIN]: Denied access to: {0} ({1} {2}) at {3} because the user does not have access to the estate",
|
|
||||||
agent.AgentID, agent.firstname, agent.lastname, RegionInfo.RegionName);
|
|
||||||
reason = String.Format("Denied access to private region {0}: You are not on the access list for that region.",
|
|
||||||
RegionInfo.RegionName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: estate/region settings are not properly hooked up
|
// TODO: estate/region settings are not properly hooked up
|
||||||
// to ILandObject.isRestrictedFromLand()
|
// to ILandObject.isRestrictedFromLand()
|
||||||
|
@ -4402,6 +4408,20 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
|
|
||||||
childAgentUpdate.ChildAgentDataUpdate(cAgentData);
|
childAgentUpdate.ChildAgentDataUpdate(cAgentData);
|
||||||
|
|
||||||
|
int ntimes = 20;
|
||||||
|
if (cAgentData.SenderWantsToWaitForRoot)
|
||||||
|
{
|
||||||
|
while (childAgentUpdate.IsChildAgent && ntimes-- > 0)
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[SCENE]: Found presence {0} {1} {2} in {3} after {4} waits",
|
||||||
|
childAgentUpdate.Name, childAgentUpdate.UUID, childAgentUpdate.IsChildAgent ? "child" : "root", RegionInfo.RegionName, 20 - ntimes);
|
||||||
|
|
||||||
|
if (childAgentUpdate.IsChildAgent)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -4459,10 +4479,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
m_log.WarnFormat(
|
m_log.WarnFormat(
|
||||||
"[SCENE PRESENCE]: Did not find presence with id {0} in {1} before timeout",
|
"[SCENE PRESENCE]: Did not find presence with id {0} in {1} before timeout",
|
||||||
agentID, RegionInfo.RegionName);
|
agentID, RegionInfo.RegionName);
|
||||||
// else
|
|
||||||
// m_log.DebugFormat(
|
|
||||||
// "[SCENE PRESENCE]: Found presence {0} {1} {2} in {3} after {4} waits",
|
|
||||||
// sp.Name, sp.UUID, sp.IsChildAgent ? "child" : "root", RegionInfo.RegionName, 10 - ntimes);
|
|
||||||
|
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
|
@ -5873,7 +5889,7 @@ Environment.Exit(1);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!AuthorizeUser(aCircuit, out reason))
|
if (!AuthorizeUser(aCircuit, false, out reason))
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("[SCENE]: Denying access for {0}", agentID);
|
// m_log.DebugFormat("[SCENE]: Denying access for {0}", agentID);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -29,7 +29,9 @@ using System;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
|
using Timer = System.Timers.Timer;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
using log4net;
|
using log4net;
|
||||||
using Nini.Config;
|
using Nini.Config;
|
||||||
|
@ -1154,9 +1156,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
MovementFlag = 0;
|
MovementFlag = 0;
|
||||||
|
|
||||||
// DIVA NOTE: I moved TriggerOnMakeRootAgent out of here and into the end of
|
m_scene.EventManager.TriggerOnMakeRootAgent(this);
|
||||||
// CompleteMovement. We don't want modules doing heavy computation before CompleteMovement
|
|
||||||
// is over.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetStateSource()
|
public int GetStateSource()
|
||||||
|
@ -1502,6 +1503,26 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool WaitForUpdateAgent(IClientAPI client)
|
||||||
|
{
|
||||||
|
// Before UpdateAgent, m_originRegionID is UUID.Zero; after, it's non-Zero
|
||||||
|
int count = 20;
|
||||||
|
while (m_originRegionID.Equals(UUID.Zero) && count-- > 0)
|
||||||
|
{
|
||||||
|
m_log.DebugFormat("[SCENE PRESENCE]: Agent {0} waiting for update in {1}", client.Name, Scene.RegionInfo.RegionName);
|
||||||
|
Thread.Sleep(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_originRegionID.Equals(UUID.Zero))
|
||||||
|
{
|
||||||
|
// Movement into region will fail
|
||||||
|
m_log.WarnFormat("[SCENE PRESENCE]: Update agent {0} never arrived", client.Name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Complete Avatar's movement into the region.
|
/// Complete Avatar's movement into the region.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1519,6 +1540,13 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
"[SCENE PRESENCE]: Completing movement of {0} into region {1} in position {2}",
|
"[SCENE PRESENCE]: Completing movement of {0} into region {1} in position {2}",
|
||||||
client.Name, Scene.RegionInfo.RegionName, AbsolutePosition);
|
client.Name, Scene.RegionInfo.RegionName, AbsolutePosition);
|
||||||
|
|
||||||
|
// Make sure it's not a login agent. We don't want to wait for updates during login
|
||||||
|
if ((m_teleportFlags & TeleportFlags.ViaLogin) == 0)
|
||||||
|
// Let's wait until UpdateAgent (called by departing region) is done
|
||||||
|
if (!WaitForUpdateAgent(client))
|
||||||
|
// The sending region never sent the UpdateAgent data, we have to refuse
|
||||||
|
return;
|
||||||
|
|
||||||
Vector3 look = Velocity;
|
Vector3 look = Velocity;
|
||||||
|
|
||||||
// if ((look.X == 0) && (look.Y == 0) && (look.Z == 0))
|
// if ((look.X == 0) && (look.Y == 0) && (look.Z == 0))
|
||||||
|
@ -1540,10 +1568,11 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
bool flying = ((m_AgentControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0);
|
bool flying = ((m_AgentControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0);
|
||||||
MakeRootAgent(AbsolutePosition, flying);
|
MakeRootAgent(AbsolutePosition, flying);
|
||||||
|
|
||||||
|
// Tell the client that we're totally ready
|
||||||
ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look);
|
ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look);
|
||||||
|
|
||||||
// Remember in HandleUseCircuitCode, we delayed this to here
|
// Remember in HandleUseCircuitCode, we delayed this to here
|
||||||
// This will also send the initial data to clients when TP to a neighboring region.
|
|
||||||
// Not ideal, but until we know we're TP-ing from a neighboring region, there's not much we can do
|
|
||||||
if (m_teleportFlags > 0)
|
if (m_teleportFlags > 0)
|
||||||
SendInitialDataToMe();
|
SendInitialDataToMe();
|
||||||
|
|
||||||
|
@ -1600,10 +1629,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
// "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms",
|
// "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms",
|
||||||
// client.Name, Scene.RegionInfo.RegionName, (DateTime.Now - startTime).Milliseconds);
|
// client.Name, Scene.RegionInfo.RegionName, (DateTime.Now - startTime).Milliseconds);
|
||||||
|
|
||||||
// DIVA NOTE: moved this here from MakeRoot. We don't want modules making heavy
|
|
||||||
// computations before CompleteMovement is over
|
|
||||||
m_scene.EventManager.TriggerOnMakeRootAgent(this);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -86,6 +86,13 @@
|
||||||
;; from the selected region_info_source.
|
;; from the selected region_info_source.
|
||||||
allow_regionless = false
|
allow_regionless = false
|
||||||
|
|
||||||
|
;; Allow child agents to see into the region even if their root counterpart isn't allowed in here
|
||||||
|
see_into_region = true
|
||||||
|
|
||||||
|
; Maximum number of position, rotation and scale changes for each prim that the simulator will store for later undos
|
||||||
|
; Increasing this number will increase memory usage.
|
||||||
|
MaxPrimUndos = 20
|
||||||
|
|
||||||
; Maximum size of non physical prims. Affects resizing of existing prims. This can be overriden in the region config file (as NonPhysicalPrimMax!).
|
; Maximum size of non physical prims. Affects resizing of existing prims. This can be overriden in the region config file (as NonPhysicalPrimMax!).
|
||||||
NonPhysicalPrimMax = 256
|
NonPhysicalPrimMax = 256
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue