varregion: extract banned region logic into a class for cleanlyness.
Add 'not found' caching in EntityTransferModule.GetRegionContainingWorldLocation so hitting borders and bad teleports do not continuiously hammer on the GridService.varregion
parent
2d2bea4aa7
commit
01c0bbf181
|
@ -121,8 +121,53 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private EntityTransferStateMachine m_entityTransferStateMachine;
|
private EntityTransferStateMachine m_entityTransferStateMachine;
|
||||||
|
|
||||||
private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions =
|
// For performance, we keed a cached of banned regions so we don't keep going
|
||||||
new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>();
|
// to the grid service.
|
||||||
|
private class BannedRegionCache
|
||||||
|
{
|
||||||
|
private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions =
|
||||||
|
new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>();
|
||||||
|
ExpiringCache<ulong, DateTime> m_idCache;
|
||||||
|
DateTime m_banUntil;
|
||||||
|
public BannedRegionCache()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
// Return 'true' if there is a valid ban entry for this agent in this region
|
||||||
|
public bool IfBanned(ulong pRegionHandle, UUID pAgentID)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache))
|
||||||
|
{
|
||||||
|
if (m_idCache.TryGetValue(pRegionHandle, out m_banUntil))
|
||||||
|
{
|
||||||
|
if (DateTime.Now < m_banUntil)
|
||||||
|
{
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
// Add this agent in this region as a banned person
|
||||||
|
public void Add(ulong pRegionHandle, UUID pAgentID)
|
||||||
|
{
|
||||||
|
if (!m_bannedRegions.TryGetValue(pAgentID, out m_idCache))
|
||||||
|
{
|
||||||
|
m_idCache = new ExpiringCache<ulong, DateTime>();
|
||||||
|
m_bannedRegions.Add(pAgentID, m_idCache, TimeSpan.FromSeconds(45));
|
||||||
|
}
|
||||||
|
m_idCache.Add(pRegionHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
|
||||||
|
}
|
||||||
|
// Remove the agent from the region's banned list
|
||||||
|
public void Remove(ulong pRegionHandle, UUID pAgentID)
|
||||||
|
{
|
||||||
|
if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache))
|
||||||
|
{
|
||||||
|
m_idCache.Remove(pRegionHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private BannedRegionCache m_bannedRegionCache = new BannedRegionCache();
|
||||||
|
|
||||||
private IEventQueue m_eqModule;
|
private IEventQueue m_eqModule;
|
||||||
private IRegionCombinerModule m_regionCombinerModule;
|
private IRegionCombinerModule m_regionCombinerModule;
|
||||||
|
@ -1393,7 +1438,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
|
|
||||||
// Call the grid service to lookup the region containing the new position.
|
// Call the grid service to lookup the region containing the new position.
|
||||||
GridRegion neighbourRegion = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID,
|
GridRegion neighbourRegion = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID,
|
||||||
presenceWorldX, presenceWorldY);
|
presenceWorldX, presenceWorldY,
|
||||||
|
Math.Max(scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY));
|
||||||
|
|
||||||
if (neighbourRegion != null)
|
if (neighbourRegion != null)
|
||||||
{
|
{
|
||||||
|
@ -1402,24 +1448,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
(float)(presenceWorldY - (double)neighbourRegion.RegionLocY),
|
(float)(presenceWorldY - (double)neighbourRegion.RegionLocY),
|
||||||
pos.Z);
|
pos.Z);
|
||||||
|
|
||||||
// Check if banned from destination region.
|
if (m_bannedRegionCache.IfBanned(neighbourRegion.RegionHandle, agentID))
|
||||||
ExpiringCache<ulong, DateTime> r;
|
|
||||||
DateTime banUntil;
|
|
||||||
if (m_bannedRegions.TryGetValue(agentID, out r))
|
|
||||||
{
|
{
|
||||||
if (r.TryGetValue(neighbourRegion.RegionHandle, out banUntil))
|
neighbourRegion = null;
|
||||||
{
|
|
||||||
if (DateTime.Now < banUntil)
|
|
||||||
{
|
|
||||||
// If we're banned from the destination, we just can't go there.
|
|
||||||
neighbourRegion = null;
|
|
||||||
}
|
|
||||||
r.Remove(neighbourRegion.RegionHandle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
r = null;
|
// If not banned, make sure this agent is not in the list.
|
||||||
|
m_bannedRegionCache.Remove(neighbourRegion.RegionHandle, agentID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if we have access to the target region.
|
// Check to see if we have access to the target region.
|
||||||
|
@ -1427,17 +1463,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
if (neighbourRegion != null
|
if (neighbourRegion != null
|
||||||
&& !scene.SimulationService.QueryAccess(neighbourRegion, agentID, newpos, out version, out reason))
|
&& !scene.SimulationService.QueryAccess(neighbourRegion, agentID, newpos, out version, out reason))
|
||||||
{
|
{
|
||||||
if (r == null)
|
// remember banned
|
||||||
{
|
m_bannedRegionCache.Add(neighbourRegion.RegionHandle, agentID);
|
||||||
r = new ExpiringCache<ulong, DateTime>();
|
|
||||||
r.Add(neighbourRegion.RegionHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
|
|
||||||
|
|
||||||
m_bannedRegions.Add(agentID, r, TimeSpan.FromSeconds(45));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
r.Add(neighbourRegion.RegionHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
|
|
||||||
}
|
|
||||||
neighbourRegion = null;
|
neighbourRegion = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1993,14 +2020,119 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
0f);
|
0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID, double px, double py)
|
||||||
|
{
|
||||||
|
// Since we don't know how big the regions could be, we have to search a very large area
|
||||||
|
// to find possible regions.
|
||||||
|
return GetRegionContainingWorldLocation(pGridService, pScopeID, px, py, Constants.MaximumRegionSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region NotFoundLocationCache class
|
||||||
|
// A collection of not found locations to make future lookups 'not found' lookups quick.
|
||||||
|
// A simple expiring cache that keeps not found locations for some number of seconds.
|
||||||
|
// A 'not found' location is presumed to be anywhere in the minimum sized region that
|
||||||
|
// contains that point. A conservitive estimate.
|
||||||
|
private class NotFoundLocationCache
|
||||||
|
{
|
||||||
|
private struct NotFoundLocation
|
||||||
|
{
|
||||||
|
public double minX, maxX, minY, maxY;
|
||||||
|
public DateTime expireTime;
|
||||||
|
}
|
||||||
|
private List<NotFoundLocation> m_notFoundLocations = new List<NotFoundLocation>();
|
||||||
|
public NotFoundLocationCache()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
// Add an area to the lost of 'not found' places. The area is the snapped region
|
||||||
|
// area around the added point.
|
||||||
|
public void Add(double pX, double pY)
|
||||||
|
{
|
||||||
|
lock (m_notFoundLocations)
|
||||||
|
{
|
||||||
|
if (!LockedContains(pX, pY))
|
||||||
|
{
|
||||||
|
NotFoundLocation nfl = new NotFoundLocation();
|
||||||
|
// A not found location is not found for at least a whole region sized area
|
||||||
|
nfl.minX = pX - (pX % (double)Constants.RegionSize);
|
||||||
|
nfl.minY = pY - (pY % (double)Constants.RegionSize);
|
||||||
|
nfl.maxX = nfl.minX + (double)Constants.RegionSize;
|
||||||
|
nfl.maxY = nfl.minY + (double)Constants.RegionSize;
|
||||||
|
nfl.expireTime = DateTime.Now + TimeSpan.FromSeconds(30);
|
||||||
|
m_notFoundLocations.Add(nfl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// Test to see of this point is in any of the 'not found' areas.
|
||||||
|
// Return 'true' if the point is found inside the 'not found' areas.
|
||||||
|
public bool Contains(double pX, double pY)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
lock (m_notFoundLocations)
|
||||||
|
ret = LockedContains(pX, pY);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
private bool LockedContains(double pX, double pY)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
this.DoExpiration();
|
||||||
|
foreach (NotFoundLocation nfl in m_notFoundLocations)
|
||||||
|
{
|
||||||
|
if (pX >= nfl.minX && pX < nfl.maxX && pY >= nfl.minY && pY < nfl.maxY)
|
||||||
|
{
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
private void DoExpiration()
|
||||||
|
{
|
||||||
|
List<NotFoundLocation> m_toRemove = null;
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
foreach (NotFoundLocation nfl in m_notFoundLocations)
|
||||||
|
{
|
||||||
|
if (nfl.expireTime < now)
|
||||||
|
{
|
||||||
|
if (m_toRemove == null)
|
||||||
|
m_toRemove = new List<NotFoundLocation>();
|
||||||
|
m_toRemove.Add(nfl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_toRemove != null)
|
||||||
|
{
|
||||||
|
foreach (NotFoundLocation nfl in m_toRemove)
|
||||||
|
m_notFoundLocations.Remove(nfl);
|
||||||
|
m_toRemove.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion // NotFoundLocationCache class
|
||||||
|
private NotFoundLocationCache m_notFoundLocationCache = new NotFoundLocationCache();
|
||||||
|
|
||||||
// Given a world position (fractional meter coordinate), get the GridRegion info for
|
// Given a world position (fractional meter coordinate), get the GridRegion info for
|
||||||
// the region containing that point.
|
// the region containing that point.
|
||||||
// Someday this should be a method on GridService.
|
// Someday this should be a method on GridService.
|
||||||
|
// 'pSizeHint' is the size of the source region but since the destination point can be anywhere
|
||||||
|
// the size of the target region is unknown thus the search area might have to be very large.
|
||||||
// Return 'null' if no such region exists.
|
// Return 'null' if no such region exists.
|
||||||
public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID, double px, double py)
|
public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID,
|
||||||
|
double px, double py, uint pSizeHint)
|
||||||
{
|
{
|
||||||
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: call, XY=<{1},{2}>", LogHeader, px, py);
|
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: call, XY=<{1},{2}>", LogHeader, px, py);
|
||||||
GridRegion ret = null;
|
GridRegion ret = null;
|
||||||
|
const double fudge = 2.0;
|
||||||
|
|
||||||
|
// One problem with this routine is negative results. That is, this can be called lots of times
|
||||||
|
// for regions that don't exist. m_notFoundLocationCache remembers 'not found' results so they
|
||||||
|
// will be quick 'not found's next time.
|
||||||
|
// NotFoundLocationCache is an expiring cache so it will eventually forget about 'not found' and
|
||||||
|
// thus re-ask the GridService about the location.
|
||||||
|
if (m_notFoundLocationCache.Contains(px, py))
|
||||||
|
{
|
||||||
|
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found via cache. loc=<{1},{2}>", LogHeader, px, py);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// As an optimization, since most regions will be legacy sized regions (256x256), first try to get
|
// As an optimization, since most regions will be legacy sized regions (256x256), first try to get
|
||||||
// the region at the appropriate legacy region location.
|
// the region at the appropriate legacy region location.
|
||||||
|
@ -2018,13 +2150,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
if (ret == null)
|
if (ret == null)
|
||||||
{
|
{
|
||||||
// If the simple lookup failed, search the larger area for a region that contains this point
|
// If the simple lookup failed, search the larger area for a region that contains this point
|
||||||
double range = (double)Constants.RegionSize * 2 + 2;
|
double range = (double)pSizeHint + fudge;
|
||||||
while (ret == null && range <= (Constants.MaximumRegionSize + Constants.RegionSize))
|
while (ret == null && range <= (Constants.MaximumRegionSize + Constants.RegionSize))
|
||||||
{
|
{
|
||||||
// Get from the grid service a list of regions that might contain this point
|
// Get from the grid service a list of regions that might contain this point.
|
||||||
|
// The region origin will be in the zero direction so only subtract the range.
|
||||||
List<GridRegion> possibleRegions = pGridService.GetRegionRange(pScopeID,
|
List<GridRegion> possibleRegions = pGridService.GetRegionRange(pScopeID,
|
||||||
(int)(px - range), (int)(px + range),
|
(int)(px - range), (int)(px),
|
||||||
(int)(py - range), (int)(py + range));
|
(int)(py - range), (int)(py));
|
||||||
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegions cnt={1}, range={2}",
|
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegions cnt={1}, range={2}",
|
||||||
LogHeader, possibleRegions.Count, range);
|
LogHeader, possibleRegions.Count, range);
|
||||||
if (possibleRegions != null && possibleRegions.Count > 0)
|
if (possibleRegions != null && possibleRegions.Count > 0)
|
||||||
|
@ -2048,6 +2181,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
range *= 2;
|
range *= 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret == null)
|
||||||
|
{
|
||||||
|
// remember this location was not found so we can quickly not find it next time
|
||||||
|
m_notFoundLocationCache.Add(px, py);
|
||||||
|
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found. Remembering loc=<{1},{2}>", LogHeader, px, py);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3425,6 +3425,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
if (!IsInTransit)
|
if (!IsInTransit)
|
||||||
{
|
{
|
||||||
Vector3 pos2 = AbsolutePosition;
|
Vector3 pos2 = AbsolutePosition;
|
||||||
|
Vector3 origPosition = pos2;
|
||||||
Vector3 vel = Velocity;
|
Vector3 vel = Velocity;
|
||||||
int neighbor = 0;
|
int neighbor = 0;
|
||||||
int[] fix = new int[2];
|
int[] fix = new int[2];
|
||||||
|
@ -3459,128 +3460,25 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
// Tried to make crossing happen but it failed.
|
// Tried to make crossing happen but it failed.
|
||||||
if (m_requestedSitTargetUUID == UUID.Zero)
|
if (m_requestedSitTargetUUID == UUID.Zero)
|
||||||
{
|
{
|
||||||
|
m_log.DebugFormat("{0} CheckForBorderCrossing: Crossing failed. Restoring old position.", LogHeader);
|
||||||
|
const float borderFudge = 0.1f;
|
||||||
|
|
||||||
Vector3 pos = AbsolutePosition;
|
if (origPosition.X < 0)
|
||||||
if (AbsolutePosition.X < 0)
|
origPosition.X = borderFudge;
|
||||||
pos.X += Velocity.X * 2;
|
else if (origPosition.X > (float)m_scene.RegionInfo.RegionSizeX)
|
||||||
else if (AbsolutePosition.X > m_scene.RegionInfo.RegionSizeX)
|
origPosition.X = (float)m_scene.RegionInfo.RegionSizeX - borderFudge;
|
||||||
pos.X -= Velocity.X * 2;
|
if (origPosition.Y < 0)
|
||||||
if (AbsolutePosition.Y < 0)
|
origPosition.Y = borderFudge;
|
||||||
pos.Y += Velocity.Y * 2;
|
else if (origPosition.Y > (float)m_scene.RegionInfo.RegionSizeY)
|
||||||
else if (AbsolutePosition.Y > m_scene.RegionInfo.RegionSizeY)
|
origPosition.Y = (float)m_scene.RegionInfo.RegionSizeY - borderFudge;
|
||||||
pos.Y -= Velocity.Y * 2;
|
|
||||||
Velocity = Vector3.Zero;
|
Velocity = Vector3.Zero;
|
||||||
AbsolutePosition = pos;
|
AbsolutePosition = origPosition;
|
||||||
|
|
||||||
AddToPhysicalScene(isFlying);
|
AddToPhysicalScene(isFlying);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// Checks if where it's headed exists a region
|
|
||||||
if (m_scene.TestBorderCross(pos2, Cardinals.W))
|
|
||||||
{
|
|
||||||
if (m_scene.TestBorderCross(pos2, Cardinals.S))
|
|
||||||
{
|
|
||||||
needsTransit = true;
|
|
||||||
neighbor = m_scene.HaveNeighbor(Cardinals.SW, ref fix);
|
|
||||||
}
|
|
||||||
else if (m_scene.TestBorderCross(pos2, Cardinals.N))
|
|
||||||
{
|
|
||||||
needsTransit = true;
|
|
||||||
neighbor = m_scene.HaveNeighbor(Cardinals.NW, ref fix);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
needsTransit = true;
|
|
||||||
neighbor = m_scene.HaveNeighbor(Cardinals.W, ref fix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (m_scene.TestBorderCross(pos2, Cardinals.E))
|
|
||||||
{
|
|
||||||
if (m_scene.TestBorderCross(pos2, Cardinals.S))
|
|
||||||
{
|
|
||||||
needsTransit = true;
|
|
||||||
neighbor = m_scene.HaveNeighbor(Cardinals.SE, ref fix);
|
|
||||||
}
|
|
||||||
else if (m_scene.TestBorderCross(pos2, Cardinals.N))
|
|
||||||
{
|
|
||||||
needsTransit = true;
|
|
||||||
neighbor = m_scene.HaveNeighbor(Cardinals.NE, ref fix);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
needsTransit = true;
|
|
||||||
neighbor = m_scene.HaveNeighbor(Cardinals.E, ref fix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (m_scene.TestBorderCross(pos2, Cardinals.S))
|
|
||||||
{
|
|
||||||
needsTransit = true;
|
|
||||||
neighbor = m_scene.HaveNeighbor(Cardinals.S, ref fix);
|
|
||||||
}
|
|
||||||
else if (m_scene.TestBorderCross(pos2, Cardinals.N))
|
|
||||||
{
|
|
||||||
needsTransit = true;
|
|
||||||
neighbor = m_scene.HaveNeighbor(Cardinals.N, ref fix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes sure avatar does not end up outside region
|
|
||||||
if (neighbor <= 0)
|
|
||||||
{
|
|
||||||
if (needsTransit)
|
|
||||||
{
|
|
||||||
if (m_requestedSitTargetUUID == UUID.Zero)
|
|
||||||
{
|
|
||||||
bool isFlying = Flying;
|
|
||||||
RemoveFromPhysicalScene();
|
|
||||||
|
|
||||||
Vector3 pos = AbsolutePosition;
|
|
||||||
if (AbsolutePosition.X < 0)
|
|
||||||
pos.X += Velocity.X * 2;
|
|
||||||
else if (AbsolutePosition.X > m_scene.RegionInfo.RegionSizeX)
|
|
||||||
pos.X -= Velocity.X * 2;
|
|
||||||
if (AbsolutePosition.Y < 0)
|
|
||||||
pos.Y += Velocity.Y * 2;
|
|
||||||
else if (AbsolutePosition.Y > m_scene.RegionInfo.RegionSizeY)
|
|
||||||
pos.Y -= Velocity.Y * 2;
|
|
||||||
Velocity = Vector3.Zero;
|
|
||||||
AbsolutePosition = pos;
|
|
||||||
|
|
||||||
// m_log.DebugFormat("[SCENE PRESENCE]: Prevented flyoff for {0} at {1}", Name, AbsolutePosition);
|
|
||||||
|
|
||||||
AddToPhysicalScene(isFlying);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (neighbor > 0)
|
|
||||||
{
|
|
||||||
if (!CrossToNewRegion())
|
|
||||||
{
|
|
||||||
if (m_requestedSitTargetUUID == UUID.Zero)
|
|
||||||
{
|
|
||||||
bool isFlying = Flying;
|
|
||||||
RemoveFromPhysicalScene();
|
|
||||||
|
|
||||||
Vector3 pos = AbsolutePosition;
|
|
||||||
if (AbsolutePosition.X < 0)
|
|
||||||
pos.X += Velocity.X * 2;
|
|
||||||
else if (AbsolutePosition.X > m_scene.RegionInfo.RegionSizeX)
|
|
||||||
pos.X -= Velocity.X * 2;
|
|
||||||
if (AbsolutePosition.Y < 0)
|
|
||||||
pos.Y += Velocity.Y * 2;
|
|
||||||
else if (AbsolutePosition.Y > m_scene.RegionInfo.RegionSizeY)
|
|
||||||
pos.Y -= Velocity.Y * 2;
|
|
||||||
Velocity = Vector3.Zero;
|
|
||||||
AbsolutePosition = pos;
|
|
||||||
|
|
||||||
AddToPhysicalScene(isFlying);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue