aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim.RegionServer/world
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim.RegionServer/world')
-rw-r--r--OpenSim.RegionServer/world/Avatar.cs54
-rw-r--r--OpenSim.RegionServer/world/Entity.cs5
-rw-r--r--OpenSim.RegionServer/world/Primitive.cs45
-rw-r--r--OpenSim.RegionServer/world/World.cs100
4 files changed, 155 insertions, 49 deletions
diff --git a/OpenSim.RegionServer/world/Avatar.cs b/OpenSim.RegionServer/world/Avatar.cs
index 2ae9992..7b79378 100644
--- a/OpenSim.RegionServer/world/Avatar.cs
+++ b/OpenSim.RegionServer/world/Avatar.cs
@@ -41,10 +41,10 @@ namespace OpenSim.world
41 m_clientThreads = clientThreads; 41 m_clientThreads = clientThreads;
42 m_regionName = regionName; 42 m_regionName = regionName;
43 m_regionHandle = regionHandle; 43 m_regionHandle = regionHandle;
44 44
45 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("Avatar.cs - Loading details from grid (DUMMY)"); 45 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("Avatar.cs - Loading details from grid (DUMMY)");
46 ControllingClient = TheClient; 46 ControllingClient = TheClient;
47 localid = 8880000 + (m_world._localNumber++); 47 localid = 8880000 + (this.m_world._localNumber++);
48 position = new LLVector3(100.0f, 100.0f, 30.0f); 48 position = new LLVector3(100.0f, 100.0f, 30.0f);
49 position.Z = m_world.LandMap[(int)position.Y * 256 + (int)position.X] + 1; 49 position.Z = m_world.LandMap[(int)position.Y * 256 + (int)position.X] + 1;
50 visualParams = new byte[218]; 50 visualParams = new byte[218];
@@ -61,7 +61,7 @@ namespace OpenSim.world
61 this.Wearables[0].ItemID = LLUUID.Random(); 61 this.Wearables[0].ItemID = LLUUID.Random();
62 62
63 this.avatarAppearanceTexture = new LLObject.TextureEntry(new LLUUID("00000000-0000-0000-5005-000000000005")); 63 this.avatarAppearanceTexture = new LLObject.TextureEntry(new LLUUID("00000000-0000-0000-5005-000000000005"));
64 64
65 } 65 }
66 66
67 public PhysicsActor PhysActor 67 public PhysicsActor PhysActor
@@ -82,7 +82,10 @@ namespace OpenSim.world
82 { 82 {
83 NewForce force = this.forcesList[i]; 83 NewForce force = this.forcesList[i];
84 PhysicsVector phyVector = new PhysicsVector(force.X, force.Y, force.Z); 84 PhysicsVector phyVector = new PhysicsVector(force.X, force.Y, force.Z);
85 this._physActor.Velocity = phyVector; 85 lock (m_world.LockPhysicsEngine)
86 {
87 this._physActor.Velocity = phyVector;
88 }
86 this.updateflag = true; 89 this.updateflag = true;
87 this.velocity = new LLVector3(force.X, force.Y, force.Z); //shouldn't really be doing this 90 this.velocity = new LLVector3(force.X, force.Y, force.Z); //shouldn't really be doing this
88 // but as we are setting the velocity (rather than using real forces) at the moment it is okay. 91 // but as we are setting the velocity (rather than using real forces) at the moment it is okay.
@@ -181,7 +184,7 @@ namespace OpenSim.world
181 AgentMovementCompletePacket mov = new AgentMovementCompletePacket(); 184 AgentMovementCompletePacket mov = new AgentMovementCompletePacket();
182 mov.AgentData.SessionID = this.ControllingClient.SessionID; 185 mov.AgentData.SessionID = this.ControllingClient.SessionID;
183 mov.AgentData.AgentID = this.ControllingClient.AgentID; 186 mov.AgentData.AgentID = this.ControllingClient.AgentID;
184 mov.Data.RegionHandle = m_regionHandle; 187 mov.Data.RegionHandle = this.m_regionHandle;
185 // TODO - dynamicalise this stuff 188 // TODO - dynamicalise this stuff
186 mov.Data.Timestamp = 1172750370; 189 mov.Data.Timestamp = 1172750370;
187 mov.Data.Position = new LLVector3(100f, 100f, 23f); 190 mov.Data.Position = new LLVector3(100f, 100f, 23f);
@@ -475,7 +478,12 @@ namespace OpenSim.world
475 ani.AnimationList[0] = new AvatarAnimationPacket.AnimationListBlock(); 478 ani.AnimationList[0] = new AvatarAnimationPacket.AnimationListBlock();
476 ani.AnimationList[0].AnimID = this.current_anim; 479 ani.AnimationList[0].AnimID = this.current_anim;
477 ani.AnimationList[0].AnimSequenceID = this.anim_seq; 480 ani.AnimationList[0].AnimSequenceID = this.anim_seq;
478 ControllingClient.OutPacket(ani); 481
482 //ControllingClient.OutPacket(ani);
483 foreach (SimClient client in m_clientThreads.Values)
484 {
485 client.OutPacket(ani);
486 }
479 } 487 }
480 488
481 //should be moved somewhere else 489 //should be moved somewhere else
@@ -522,7 +530,11 @@ namespace OpenSim.world
522 ImprovedTerseObjectUpdatePacket.ObjectDataBlock dat = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock(); 530 ImprovedTerseObjectUpdatePacket.ObjectDataBlock dat = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock();
523 531
524 dat.TextureEntry = new byte[0];// AvatarTemplate.TextureEntry; 532 dat.TextureEntry = new byte[0];// AvatarTemplate.TextureEntry;
525 libsecondlife.LLVector3 pos2 = new LLVector3(this._physActor.Position.X, this._physActor.Position.Y, this._physActor.Position.Z); 533 libsecondlife.LLVector3 pos2 = new LLVector3(0, 0, 0);
534 lock (m_world.LockPhysicsEngine)
535 {
536 pos2 = new LLVector3(this._physActor.Position.X, this._physActor.Position.Y, this._physActor.Position.Z);
537 }
526 538
527 uint ID = this.localid; 539 uint ID = this.localid;
528 540
@@ -542,8 +554,11 @@ namespace OpenSim.world
542 ushort InternVelocityX; 554 ushort InternVelocityX;
543 ushort InternVelocityY; 555 ushort InternVelocityY;
544 ushort InternVelocityZ; 556 ushort InternVelocityZ;
545 557 Axiom.MathLib.Vector3 internDirec = new Axiom.MathLib.Vector3(0, 0, 0);
546 Axiom.MathLib.Vector3 internDirec = new Axiom.MathLib.Vector3(this._physActor.Velocity.X, this._physActor.Velocity.Y, this._physActor.Velocity.Z); 558 lock (m_world.LockPhysicsEngine)
559 {
560 internDirec = new Axiom.MathLib.Vector3(this._physActor.Velocity.X, this._physActor.Velocity.Y, this._physActor.Velocity.Z);
561 }
547 internDirec = internDirec / 128.0f; 562 internDirec = internDirec / 128.0f;
548 internDirec.x += 1; 563 internDirec.x += 1;
549 internDirec.y += 1; 564 internDirec.y += 1;
@@ -596,6 +611,27 @@ namespace OpenSim.world
596 Avatar.Animations = new AvatarAnimations(); 611 Avatar.Animations = new AvatarAnimations();
597 Avatar.Animations.LoadAnims(); 612 Avatar.Animations.LoadAnims();
598 } 613 }
614
615 public override void LandRenegerated()
616 {
617 position = new LLVector3(100.0f, 100.0f, 30.0f);
618 position.Z = this.m_world.LandMap[(int)position.Y * 256 + (int)position.X] + 50;
619 if (this._physActor != null)
620 {
621 try
622 {
623 lock (this.m_world.LockPhysicsEngine)
624 {
625
626 this._physActor.Position = new PhysicsVector(position.X, position.Y, position.Z);
627 }
628 }
629 catch (Exception e)
630 {
631 Console.WriteLine(e.Message);
632 }
633 }
634 }
599 } 635 }
600 636
601 public class NewForce 637 public class NewForce
diff --git a/OpenSim.RegionServer/world/Entity.cs b/OpenSim.RegionServer/world/Entity.cs
index 780f3a0..567c0b7 100644
--- a/OpenSim.RegionServer/world/Entity.cs
+++ b/OpenSim.RegionServer/world/Entity.cs
@@ -63,5 +63,10 @@ namespace OpenSim.world
63 { 63 {
64 64
65 } 65 }
66
67 public virtual void LandRenegerated()
68 {
69
70 }
66 } 71 }
67} 72}
diff --git a/OpenSim.RegionServer/world/Primitive.cs b/OpenSim.RegionServer/world/Primitive.cs
index a185f0b..ca764a7 100644
--- a/OpenSim.RegionServer/world/Primitive.cs
+++ b/OpenSim.RegionServer/world/Primitive.cs
@@ -12,6 +12,7 @@ namespace OpenSim.world
12{ 12{
13 public class Primitive : Entity 13 public class Primitive : Entity
14 { 14 {
15 //private static object physicsLock = new object();
15 protected float mesh_cutbegin; 16 protected float mesh_cutbegin;
16 protected float mesh_cutend; 17 protected float mesh_cutend;
17 protected PrimData primData; 18 protected PrimData primData;
@@ -21,7 +22,8 @@ namespace OpenSim.world
21 private ObjectUpdatePacket OurPacket; 22 private ObjectUpdatePacket OurPacket;
22 private PhysicsActor _physActor; 23 private PhysicsActor _physActor;
23 private bool physicsEnabled = false; 24 private bool physicsEnabled = false;
24 private bool physicstest = false; //just added for testing 25 private bool physicstest = false;
26 private LLVector3 positionLastFrame = new LLVector3(0, 0, 0);
25 private Dictionary<uint, SimClient> m_clientThreads; 27 private Dictionary<uint, SimClient> m_clientThreads;
26 private ulong m_regionHandle; 28 private ulong m_regionHandle;
27 private World m_world; 29 private World m_world;
@@ -72,7 +74,7 @@ namespace OpenSim.world
72 { 74 {
73 mesh_cutbegin = 0.0f; 75 mesh_cutbegin = 0.0f;
74 mesh_cutend = 1.0f; 76 mesh_cutend = 1.0f;
75 77
76 m_clientThreads = clientThreads; 78 m_clientThreads = clientThreads;
77 m_regionHandle = regionHandle; 79 m_regionHandle = regionHandle;
78 m_world = world; 80 m_world = world;
@@ -97,13 +99,30 @@ namespace OpenSim.world
97 this.position = pos; 99 this.position = pos;
98 if (this._physActor != null) // && this.physicsEnabled) 100 if (this._physActor != null) // && this.physicsEnabled)
99 { 101 {
100 this._physActor.Position = new PhysicsVector(pos.X, pos.Y, pos.Z); 102 try
103 {
104 lock (m_world.LockPhysicsEngine)
105 {
106 this._physActor.Position = new PhysicsVector(pos.X, pos.Y, pos.Z);
107 }
108 }
109 catch (Exception e)
110 {
111 Console.WriteLine(e.Message);
112 }
101 } 113 }
102 this.updateFlag = true; 114 this.updateFlag = true;
103 } 115 }
104 116
105 public override void update() 117 public override void update()
106 { 118 {
119 LLVector3 pos2 = new LLVector3(0, 0, 0);
120 if (this._physActor != null && this.physicsEnabled)
121 {
122
123 PhysicsVector pPos = this._physActor.Position;
124 pos2 = new LLVector3(pPos.X, pPos.Y, pPos.Z);
125 }
107 if (this.newPrimFlag) 126 if (this.newPrimFlag)
108 { 127 {
109 foreach (SimClient client in m_clientThreads.Values) 128 foreach (SimClient client in m_clientThreads.Values)
@@ -137,15 +156,19 @@ namespace OpenSim.world
137 { 156 {
138 if (this._physActor != null && this.physicsEnabled) 157 if (this._physActor != null && this.physicsEnabled)
139 { 158 {
140 ImprovedTerseObjectUpdatePacket terse = new ImprovedTerseObjectUpdatePacket(); 159 if (pos2 != this.positionLastFrame)
141 terse.RegionData.RegionHandle = m_regionHandle; // FIXME
142 terse.RegionData.TimeDilation = 64096;
143 terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];
144 terse.ObjectData[0] = this.CreateImprovedBlock();
145 foreach (SimClient client in m_clientThreads.Values)
146 { 160 {
147 client.OutPacket(terse); 161 ImprovedTerseObjectUpdatePacket terse = new ImprovedTerseObjectUpdatePacket();
162 terse.RegionData.RegionHandle = m_regionHandle; // FIXME
163 terse.RegionData.TimeDilation = 64096;
164 terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];
165 terse.ObjectData[0] = this.CreateImprovedBlock();
166 foreach (SimClient client in m_clientThreads.Values)
167 {
168 client.OutPacket(terse);
169 }
148 } 170 }
171 this.positionLastFrame = pos2;
149 } 172 }
150 } 173 }
151 174
@@ -487,7 +510,7 @@ namespace OpenSim.world
487 this.primData.LocalID = this.localid; 510 this.primData.LocalID = this.localid;
488 this.primData.Position = this.position; 511 this.primData.Position = this.position;
489 this.primData.Rotation = new LLQuaternion(this.rotation.x, this.rotation.y, this.rotation.z, this.rotation.w); 512 this.primData.Rotation = new LLQuaternion(this.rotation.x, this.rotation.y, this.rotation.z, this.rotation.w);
490 m_world.localStorage.StorePrim(this.primData); 513 this.m_world.localStorage.StorePrim(this.primData);
491 } 514 }
492 } 515 }
493 516
diff --git a/OpenSim.RegionServer/world/World.cs b/OpenSim.RegionServer/world/World.cs
index 0d126e6..3c131b2 100644
--- a/OpenSim.RegionServer/world/World.cs
+++ b/OpenSim.RegionServer/world/World.cs
@@ -14,6 +14,7 @@ namespace OpenSim.world
14{ 14{
15 public class World : ILocalStorageReceiver 15 public class World : ILocalStorageReceiver
16 { 16 {
17 public object LockPhysicsEngine = new object();
17 public Dictionary<libsecondlife.LLUUID, Entity> Entities; 18 public Dictionary<libsecondlife.LLUUID, Entity> Entities;
18 public float[] LandMap; 19 public float[] LandMap;
19 public ScriptEngine Scripts; 20 public ScriptEngine Scripts;
@@ -25,19 +26,19 @@ namespace OpenSim.world
25 private Random Rand = new Random(); 26 private Random Rand = new Random();
26 private uint _primCount = 702000; 27 private uint _primCount = 702000;
27 private int storageCount; 28 private int storageCount;
28 private Dictionary<uint, SimClient> m_clientThreads; 29 private Dictionary<uint, SimClient> m_clientThreads;
29 private ulong m_regionHandle; 30 private ulong m_regionHandle;
30 private string m_regionName; 31 private string m_regionName;
31 private SimConfig m_cfg; 32 private SimConfig m_cfg;
32 33
33 public World(Dictionary<uint, SimClient> clientThreads, ulong regionHandle, string regionName, SimConfig cfg) 34 public World(Dictionary<uint, SimClient> clientThreads, ulong regionHandle, string regionName, SimConfig cfg)
34 { 35 {
35 m_clientThreads = clientThreads; 36 m_clientThreads = clientThreads;
36 m_regionHandle = regionHandle; 37 m_regionHandle = regionHandle;
37 m_regionName = regionName; 38 m_regionName = regionName;
38 m_cfg = cfg; 39 m_cfg = cfg;
39 40
40 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs - creating new entitities instance"); 41 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs - creating new entitities instance");
41 Entities = new Dictionary<libsecondlife.LLUUID, Entity>(); 42 Entities = new Dictionary<libsecondlife.LLUUID, Entity>();
42 43
43 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs - creating LandMap"); 44 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs - creating LandMap");
@@ -73,8 +74,11 @@ namespace OpenSim.world
73 { 74 {
74 Entities[UUID].addForces(); 75 Entities[UUID].addForces();
75 } 76 }
76 77
77 this.phyScene.Simulate(timeStep); 78 lock (this.LockPhysicsEngine)
79 {
80 this.phyScene.Simulate(timeStep);
81 }
78 82
79 foreach (libsecondlife.LLUUID UUID in Entities.Keys) 83 foreach (libsecondlife.LLUUID UUID in Entities.Keys)
80 { 84 {
@@ -118,18 +122,49 @@ namespace OpenSim.world
118 this.localStorage = store; 122 this.localStorage = store;
119 return(store == null); 123 return(store == null);
120 } 124 }
125
126 public void RegenerateTerrain()
127 {
128 HeightmapGenHills hills = new HeightmapGenHills();
129 this.LandMap = hills.GenerateHeightmap(200, 4.0f, 80.0f, false);
130 lock (this.LockPhysicsEngine)
131 {
132 this.phyScene.SetTerrain(this.LandMap);
133 }
134 m_cfg.SaveMap(this.LandMap);
135
136 foreach (SimClient client in m_clientThreads.Values)
137 {
138 this.SendLayerData(client);
139 }
140
141 foreach (libsecondlife.LLUUID UUID in Entities.Keys)
142 {
143 Entities[UUID].LandRenegerated();
144 }
145 }
146
147 public void RegenerateTerrain(float[] newMap)
148 {
149
150 this.LandMap = newMap;
151 lock (this.LockPhysicsEngine)
152 {
153 this.phyScene.SetTerrain(this.LandMap);
154 }
155 m_cfg.SaveMap(this.LandMap);
156
157 foreach (SimClient client in m_clientThreads.Values)
158 {
159 this.SendLayerData(client);
160 }
161
162 foreach (libsecondlife.LLUUID UUID in Entities.Keys)
163 {
164 Entities[UUID].LandRenegerated();
165 }
166 }
121 167
122 public void RegenerateTerrain()
123 {
124 HeightmapGenHills hills = new HeightmapGenHills();
125 this.LandMap = hills.GenerateHeightmap(200, 4.0f, 80.0f, false);
126 this.phyScene.SetTerrain(this.LandMap);
127 m_cfg.SaveMap(this.LandMap);
128
129 foreach(SimClient client in m_clientThreads.Values) {
130 this.SendLayerData(client);
131 }
132 }
133 public void LoadPrimsFromStorage() 168 public void LoadPrimsFromStorage()
134 { 169 {
135 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs: LoadPrimsFromStorage() - Loading primitives"); 170 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs: LoadPrimsFromStorage() - Loading primitives");
@@ -143,7 +178,7 @@ namespace OpenSim.world
143 _primCount = prim.LocalID + 1; 178 _primCount = prim.LocalID + 1;
144 } 179 }
145 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs: PrimFromStorage() - Reloading prim (localId "+ prim.LocalID+ " ) from storage"); 180 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs: PrimFromStorage() - Reloading prim (localId "+ prim.LocalID+ " ) from storage");
146 Primitive nPrim = new Primitive(m_clientThreads, m_regionHandle, this); 181 Primitive nPrim = new Primitive(m_clientThreads, m_regionHandle, this);
147 nPrim.CreateFromStorage(prim); 182 nPrim.CreateFromStorage(prim);
148 this.Entities.Add(nPrim.uuid, nPrim); 183 this.Entities.Add(nPrim.uuid, nPrim);
149 } 184 }
@@ -182,27 +217,34 @@ namespace OpenSim.world
182 } 217 }
183 } 218 }
184 219
185 public void AddViewerAgent(SimClient AgentClient) { 220 public void AddViewerAgent(SimClient AgentClient)
221 {
186 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs:AddViewerAgent() - Creating new avatar for remote viewer agent"); 222 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs:AddViewerAgent() - Creating new avatar for remote viewer agent");
187 Avatar NewAvatar = new Avatar(AgentClient, this, m_regionName, m_clientThreads, m_regionHandle ); 223 Avatar NewAvatar = new Avatar(AgentClient, this, m_regionName, m_clientThreads, m_regionHandle);
188 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs:AddViewerAgent() - Adding new avatar to world"); 224 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs:AddViewerAgent() - Adding new avatar to world");
189 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs:AddViewerAgent() - Starting RegionHandshake "); 225 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs:AddViewerAgent() - Starting RegionHandshake ");
190 NewAvatar.SendRegionHandshake(this); 226 NewAvatar.SendRegionHandshake(this);
191 PhysicsVector pVec = new PhysicsVector(NewAvatar.position.X, NewAvatar.position.Y, NewAvatar.position.Z); 227 PhysicsVector pVec = new PhysicsVector(NewAvatar.position.X, NewAvatar.position.Y, NewAvatar.position.Z);
192 NewAvatar.PhysActor = this.phyScene.AddAvatar(pVec); 228 lock (this.LockPhysicsEngine)
229 {
230 NewAvatar.PhysActor = this.phyScene.AddAvatar(pVec);
231 }
193 this.Entities.Add(AgentClient.AgentID, NewAvatar); 232 this.Entities.Add(AgentClient.AgentID, NewAvatar);
194 } 233 }
195 234
196 public void AddNewPrim(ObjectAddPacket addPacket, SimClient AgentClient) 235 public void AddNewPrim(ObjectAddPacket addPacket, SimClient AgentClient)
197 { 236 {
198 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs: AddNewPrim() - Creating new prim"); 237 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("World.cs: AddNewPrim() - Creating new prim");
199 Primitive prim = new Primitive(m_clientThreads, m_regionHandle, this ); 238 Primitive prim = new Primitive(m_clientThreads, m_regionHandle, this);
200 prim.CreateFromPacket(addPacket, AgentClient.AgentID, this._primCount); 239 prim.CreateFromPacket(addPacket, AgentClient.AgentID, this._primCount);
201 PhysicsVector pVec = new PhysicsVector(prim.position.X, prim.position.Y, prim.position.Z); 240 PhysicsVector pVec = new PhysicsVector(prim.position.X, prim.position.Y, prim.position.Z);
202 PhysicsVector pSize = new PhysicsVector( 0.255f, 0.255f, 0.255f); 241 PhysicsVector pSize = new PhysicsVector( 0.255f, 0.255f, 0.255f);
203 if(OpenSim.world.Avatar.PhysicsEngineFlying) 242 if(OpenSim.world.Avatar.PhysicsEngineFlying)
204 { 243 {
205 prim.PhysActor = this.phyScene.AddPrim(pVec, pSize ); 244 lock (this.LockPhysicsEngine)
245 {
246 prim.PhysActor = this.phyScene.AddPrim(pVec, pSize);
247 }
206 } 248 }
207 //prim.PhysicsEnabled = true; 249 //prim.PhysicsEnabled = true;
208 this.Entities.Add(prim.uuid, prim); 250 this.Entities.Add(prim.uuid, prim);
@@ -243,9 +285,9 @@ namespace OpenSim.world
243 } 285 }
244 foreach( libsecondlife.LLUUID uuid in DeRezEnts ) 286 foreach( libsecondlife.LLUUID uuid in DeRezEnts )
245 { 287 {
246 lock (this.Entities) 288 lock (Entities)
247 { 289 {
248 this.Entities.Remove(uuid); 290 Entities.Remove(uuid);
249 } 291 }
250 } 292 }
251 293