aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/world
diff options
context:
space:
mode:
authorMW2007-03-02 18:12:54 +0000
committerMW2007-03-02 18:12:54 +0000
commit40a52b0f55828bfa564e1b044aa99ebff76a2b7e (patch)
tree3d1bd27c45609e71f4450e99bba5984e06c00bb2 /src/world
parent* Bind to IPAddress.Any instead of a hardcoded address (diff)
downloadopensim-SC-40a52b0f55828bfa564e1b044aa99ebff76a2b7e.zip
opensim-SC-40a52b0f55828bfa564e1b044aa99ebff76a2b7e.tar.gz
opensim-SC-40a52b0f55828bfa564e1b044aa99ebff76a2b7e.tar.bz2
opensim-SC-40a52b0f55828bfa564e1b044aa99ebff76a2b7e.tar.xz
A example of how the physics plugins might work
Diffstat (limited to 'src/world')
-rw-r--r--src/world/Avatar.cs347
-rw-r--r--src/world/Entity.cs14
-rw-r--r--src/world/World.cs30
3 files changed, 253 insertions, 138 deletions
diff --git a/src/world/Avatar.cs b/src/world/Avatar.cs
index bcf79a8..ec51c01 100644
--- a/src/world/Avatar.cs
+++ b/src/world/Avatar.cs
@@ -4,148 +4,231 @@ using System.IO;
4using System.Text; 4using System.Text;
5using libsecondlife; 5using libsecondlife;
6using libsecondlife.Packets; 6using libsecondlife.Packets;
7using PhysicsManager;
7 8
8namespace OpenSim.world 9namespace OpenSim.world
9{ 10{
10 public class Avatar : Entity 11 public class Avatar : Entity
11 { 12 {
12 public string firstname; 13 public string firstname;
13 public string lastname; 14 public string lastname;
14 public OpenSimClient ControllingClient; 15 public OpenSimClient ControllingClient;
15 private libsecondlife.Packets.ObjectUpdatePacket.ObjectDataBlock AvatarTemplate; 16 private PhysicsActor _physActor;
16 17 private libsecondlife.Packets.ObjectUpdatePacket.ObjectDataBlock AvatarTemplate;
17 public Avatar(OpenSimClient TheClient) { 18 private bool updateflag;
18 Console.WriteLine("Avatar.cs - Loading details from grid (DUMMY)"); 19 private bool walking;
19 ControllingClient=TheClient; 20 private List<NewForce> forcesList = new List<NewForce>();
20 SetupTemplate("avatar-template.dat"); 21
21 } 22 public Avatar(OpenSimClient TheClient) {
22 23 Console.WriteLine("Avatar.cs - Loading details from grid (DUMMY)");
23 private void SetupTemplate(string name) 24 ControllingClient=TheClient;
25 SetupTemplate("avatar-template.dat");
26 }
27
28 public PhysicsActor PhysActor
29 {
30 set
31 {
32 this._physActor = value;
33 }
34 }
35 public override void addFroces()
24 { 36 {
25 37 if(this.forcesList.Count>0)
26 int i = 0; 38 {
27 FileInfo fInfo = new FileInfo(name); 39 for(int i=0 ; i < this.forcesList.Count; i++)
28 long numBytes = fInfo.Length; 40 {
29 FileStream fStream = new FileStream(name, FileMode.Open, FileAccess.Read); 41 NewForce force = this.forcesList[i];
30 BinaryReader br = new BinaryReader(fStream); 42 PhysicsVector phyVector = new PhysicsVector(force.X, force.Y, force.Z);
31 byte [] data1 = br.ReadBytes((int)numBytes); 43 this.PhysActor.Velocity = phyVector;
32 br.Close(); 44 this.updateflag = true;
33 fStream.Close(); 45 this.velocity = new Axiom.MathLib.Vector3(force.X, force.Y, force.Z); //shouldn't really be doing this
34 46 // but as we are setting the velocity (rather than using real forces) at the moment it is okay.
35 libsecondlife.Packets.ObjectUpdatePacket.ObjectDataBlock objdata = new libsecondlife.Packets.ObjectUpdatePacket.ObjectDataBlock(data1, ref i); 47
36 48 }
37 System.Text.Encoding enc = System.Text.Encoding.ASCII; 49 }
38 libsecondlife.LLVector3 pos = new LLVector3(objdata.ObjectData, 16);
39 pos.X = 100f;
40 objdata.ID = 8880000;
41 objdata.NameValue = enc.GetBytes("FirstName STRING RW SV Test \nLastName STRING RW SV User \0");
42 libsecondlife.LLVector3 pos2 = new LLVector3(100f,100f,23f);
43 //objdata.FullID=user.AgentID;
44 byte[] pb = pos.GetBytes();
45 Array.Copy(pb, 0, objdata.ObjectData, 16, pb.Length);
46
47 AvatarTemplate = objdata;
48
49 } 50 }
51
52 public override void update()
53 {
54 if(this.updateflag)
55 {
56 //need to send movement info
57 //so create the improvedterseobjectupdate packet
58 }
59 }
50 60
51 public void CompleteMovement(World RegionInfo) { 61 private void SetupTemplate(string name)
52 Console.WriteLine("Avatar.cs:CompleteMovement() - Constructing AgentMovementComplete packet"); 62 {
53 AgentMovementCompletePacket mov = new AgentMovementCompletePacket(); 63
54 mov.AgentData.SessionID = this.ControllingClient.SessionID; 64 int i = 0;
55 mov.AgentData.AgentID = this.ControllingClient.AgentID; 65 FileInfo fInfo = new FileInfo(name);
56 mov.Data.RegionHandle = OpenSim_Main.cfg.RegionHandle; 66 long numBytes = fInfo.Length;
57 // TODO - dynamicalise this stuff 67 FileStream fStream = new FileStream(name, FileMode.Open, FileAccess.Read);
58 mov.Data.Timestamp = 1172750370; 68 BinaryReader br = new BinaryReader(fStream);
59 mov.Data.Position = new LLVector3(100f, 100f, 23f); 69 byte [] data1 = br.ReadBytes((int)numBytes);
60 mov.Data.LookAt = new LLVector3(0.99f, 0.042f, 0); 70 br.Close();
61 71 fStream.Close();
62 Console.WriteLine("Sending AgentMovementComplete packet"); 72
63 ControllingClient.OutPacket(mov); 73 libsecondlife.Packets.ObjectUpdatePacket.ObjectDataBlock objdata = new libsecondlife.Packets.ObjectUpdatePacket.ObjectDataBlock(data1, ref i);
64 } 74
75 System.Text.Encoding enc = System.Text.Encoding.ASCII;
76 libsecondlife.LLVector3 pos = new LLVector3(objdata.ObjectData, 16);
77 pos.X = 100f;
78 objdata.ID = 8880000;
79 objdata.NameValue = enc.GetBytes("FirstName STRING RW SV Test \nLastName STRING RW SV User \0");
80 libsecondlife.LLVector3 pos2 = new LLVector3(100f,100f,23f);
81 //objdata.FullID=user.AgentID;
82 byte[] pb = pos.GetBytes();
83 Array.Copy(pb, 0, objdata.ObjectData, 16, pb.Length);
84
85 AvatarTemplate = objdata;
86
87 }
65 88
66 public void SendInitialPosition() { 89 public void CompleteMovement(World RegionInfo) {
67 System.Text.Encoding _enc = System.Text.Encoding.ASCII; 90 Console.WriteLine("Avatar.cs:CompleteMovement() - Constructing AgentMovementComplete packet");
68 91 AgentMovementCompletePacket mov = new AgentMovementCompletePacket();
92 mov.AgentData.SessionID = this.ControllingClient.SessionID;
93 mov.AgentData.AgentID = this.ControllingClient.AgentID;
94 mov.Data.RegionHandle = OpenSim_Main.cfg.RegionHandle;
95 // TODO - dynamicalise this stuff
96 mov.Data.Timestamp = 1172750370;
97 mov.Data.Position = new LLVector3(100f, 100f, 23f);
98 mov.Data.LookAt = new LLVector3(0.99f, 0.042f, 0);
99
100 Console.WriteLine("Sending AgentMovementComplete packet");
101 ControllingClient.OutPacket(mov);
102 }
69 103
70 //send a objectupdate packet with information about the clients avatar 104 public void SendInitialPosition() {
71 ObjectUpdatePacket objupdate = new ObjectUpdatePacket(); 105 System.Text.Encoding _enc = System.Text.Encoding.ASCII;
72 objupdate.RegionData.RegionHandle = OpenSim_Main.cfg.RegionHandle; 106
73 objupdate.RegionData.TimeDilation = 64096;
74 objupdate.ObjectData = new libsecondlife.Packets.ObjectUpdatePacket.ObjectDataBlock[1];
75
76 objupdate.ObjectData[0] = AvatarTemplate;
77 //give this avatar object a local id and assign the user a name
78 objupdate.ObjectData[0].ID = 8880000 + OpenSim_Main.local_world._localNumber;
79 //User_info.name="Test"+this.local_numer+" User";
80 objupdate.ObjectData[0].FullID = ControllingClient.AgentID;
81 objupdate.ObjectData[0].NameValue = _enc.GetBytes("FirstName STRING RW SV " + firstname + "\nLastName STRING RW SV " + lastname + " \0");
82
83 libsecondlife.LLVector3 pos2 = new LLVector3(100f, 100.0f, 23.0f);
84
85 byte[] pb = pos2.GetBytes();
86
87 Array.Copy(pb, 0, objupdate.ObjectData[0].ObjectData, 16, pb.Length);
88 OpenSim_Main.local_world._localNumber++;
89 this.ControllingClient.OutPacket(objupdate);
90 }
91
92 public void SendInitialAppearance() {
93 AgentWearablesUpdatePacket aw = new AgentWearablesUpdatePacket();
94 aw.AgentData.AgentID = this.ControllingClient.AgentID;
95 aw.AgentData.SerialNum = 0;
96 aw.AgentData.SessionID = ControllingClient.SessionID;
97
98 aw.WearableData = new AgentWearablesUpdatePacket.WearableDataBlock[13];
99 AgentWearablesUpdatePacket.WearableDataBlock awb = new AgentWearablesUpdatePacket.WearableDataBlock();
100 awb.WearableType = (byte)0;
101 awb.AssetID = new LLUUID("66c41e39-38f9-f75a-024e-585989bfab73");
102 awb.ItemID = LLUUID.Random();
103 aw.WearableData[0] = awb;
104
105 for(int i=1; i<13; i++) {
106 awb = new AgentWearablesUpdatePacket.WearableDataBlock();
107 awb.WearableType = (byte)i;
108 awb.AssetID = new LLUUID("00000000-0000-0000-0000-000000000000");
109 awb.ItemID = new LLUUID("00000000-0000-0000-0000-000000000000");
110 aw.WearableData[i] = awb;
111 }
112
113 ControllingClient.OutPacket(aw);
114 }
115 107
116 public void SendRegionHandshake(World RegionInfo) { 108 //send a objectupdate packet with information about the clients avatar
117 Console.WriteLine("Avatar.cs:SendRegionHandshake() - Creating empty RegionHandshake packet"); 109 ObjectUpdatePacket objupdate = new ObjectUpdatePacket();
118 System.Text.Encoding _enc = System.Text.Encoding.ASCII; 110 objupdate.RegionData.RegionHandle = OpenSim_Main.cfg.RegionHandle;
119 RegionHandshakePacket handshake = new RegionHandshakePacket(); 111 objupdate.RegionData.TimeDilation = 64096;
120 112 objupdate.ObjectData = new libsecondlife.Packets.ObjectUpdatePacket.ObjectDataBlock[1];
121 Console.WriteLine("Avatar.cs:SendRegionhandshake() - Filling in RegionHandshake details"); 113
122 handshake.RegionInfo.BillableFactor = 0; 114 objupdate.ObjectData[0] = AvatarTemplate;
123 handshake.RegionInfo.IsEstateManager = false; 115 //give this avatar object a local id and assign the user a name
124 handshake.RegionInfo.TerrainHeightRange00 = 60; 116 objupdate.ObjectData[0].ID = 8880000 + OpenSim_Main.local_world._localNumber;
125 handshake.RegionInfo.TerrainHeightRange01 = 60; 117 //User_info.name="Test"+this.local_numer+" User";
126 handshake.RegionInfo.TerrainHeightRange10 = 60; 118 objupdate.ObjectData[0].FullID = ControllingClient.AgentID;
127 handshake.RegionInfo.TerrainHeightRange11 = 60; 119 objupdate.ObjectData[0].NameValue = _enc.GetBytes("FirstName STRING RW SV " + firstname + "\nLastName STRING RW SV " + lastname + " \0");
128 handshake.RegionInfo.TerrainStartHeight00 = 10; 120
129 handshake.RegionInfo.TerrainStartHeight01 = 10; 121 libsecondlife.LLVector3 pos2 = new LLVector3(100f, 100.0f, 23.0f);
130 handshake.RegionInfo.TerrainStartHeight10 = 10; 122
131 handshake.RegionInfo.TerrainStartHeight11 = 10; 123 byte[] pb = pos2.GetBytes();
132 handshake.RegionInfo.SimAccess = 13; 124
133 handshake.RegionInfo.WaterHeight = 5; 125 Array.Copy(pb, 0, objupdate.ObjectData[0].ObjectData, 16, pb.Length);
134 handshake.RegionInfo.RegionFlags = 72458694; 126 OpenSim_Main.local_world._localNumber++;
135 handshake.RegionInfo.SimName = _enc.GetBytes(OpenSim_Main.cfg.RegionName + "\0"); 127 this.ControllingClient.OutPacket(objupdate);
136 handshake.RegionInfo.SimOwner = new LLUUID("00000000-0000-0000-0000-000000000000"); 128 }
137 handshake.RegionInfo.TerrainBase0 = new LLUUID("b8d3965a-ad78-bf43-699b-bff8eca6c975"); 129
138 handshake.RegionInfo.TerrainBase1 = new LLUUID("abb783e6-3e93-26c0-248a-247666855da3"); 130 public void SendInitialAppearance() {
139 handshake.RegionInfo.TerrainBase2 = new LLUUID("179cdabd-398a-9b6b-1391-4dc333ba321f"); 131 AgentWearablesUpdatePacket aw = new AgentWearablesUpdatePacket();
140 handshake.RegionInfo.TerrainBase3 = new LLUUID("beb169c7-11ea-fff2-efe5-0f24dc881df2"); 132 aw.AgentData.AgentID = this.ControllingClient.AgentID;
141 handshake.RegionInfo.TerrainDetail0 = new LLUUID("00000000-0000-0000-0000-000000000000"); 133 aw.AgentData.SerialNum = 0;
142 handshake.RegionInfo.TerrainDetail1 = new LLUUID("00000000-0000-0000-0000-000000000000"); 134 aw.AgentData.SessionID = ControllingClient.SessionID;
143 handshake.RegionInfo.TerrainDetail2 = new LLUUID("00000000-0000-0000-0000-000000000000"); 135
144 handshake.RegionInfo.TerrainDetail3 = new LLUUID("00000000-0000-0000-0000-000000000000"); 136 aw.WearableData = new AgentWearablesUpdatePacket.WearableDataBlock[13];
145 handshake.RegionInfo.CacheID = new LLUUID("545ec0a5-5751-1026-8a0b-216e38a7ab37"); 137 AgentWearablesUpdatePacket.WearableDataBlock awb = new AgentWearablesUpdatePacket.WearableDataBlock();
146 138 awb.WearableType = (byte)0;
147 Console.WriteLine("Avatar.cs:SendRegionHandshake() - Sending RegionHandshake packet"); 139 awb.AssetID = new LLUUID("66c41e39-38f9-f75a-024e-585989bfab73");
148 this.ControllingClient.OutPacket(handshake); 140 awb.ItemID = LLUUID.Random();
149 } 141 aw.WearableData[0] = awb;
142
143 for(int i=1; i<13; i++) {
144 awb = new AgentWearablesUpdatePacket.WearableDataBlock();
145 awb.WearableType = (byte)i;
146 awb.AssetID = new LLUUID("00000000-0000-0000-0000-000000000000");
147 awb.ItemID = new LLUUID("00000000-0000-0000-0000-000000000000");
148 aw.WearableData[i] = awb;
149 }
150
151 ControllingClient.OutPacket(aw);
152 }
153
154 public void HandleUpdate(AgentUpdatePacket pack) {
155 if(((uint)pack.AgentData.ControlFlags & (uint)MainAvatar.AgentUpdateFlags.AGENT_CONTROL_AT_POS) !=0) {
156 if(!walking)
157 {
158 //we should add a new force to the list
159 // but for now we will deal with velocities
160 NewFoce newVelocity = new NewForce();
161 Axiom.MathLib.Vector3 v3 = new Axiom.MathLib.Vector3(1, 0, 0);
162 Axiom.MathLib.Quaternion q = new Axiom.MathLib.Quaternion(pack.AgentData.BodyRotation.W, pack.AgentData.BodyRotation.X, pack.AgentData.BodyRotation.Y, pack.AgentData.BodyRotation.Z);
163 Axiom.MathLib.Vector3 direc = q * v3;
164 direc.Normalize();
165
166 Axiom.MathLib.Vector3 internDirec = new Vector3(direc.x, direc.y, direc.z);
167
168 //work out velocity for sim physics system
169 direc = direc * ((0.03f) * 128f);
170 newVelocity.X = direc.x;
171 newVelocity.Y = direc.y;
172 newVelocity.Z = direc.z;
173 this.forcesList.Add(newVelocity);
174 walking=true;
175 }
176 }
177 else
178 {
179 if(walking)
180 {
181
182 }
183 }
184 }
185
186 //should be moved somewhere else
187 public void SendRegionHandshake(World RegionInfo) {
188 Console.WriteLine("Avatar.cs:SendRegionHandshake() - Creating empty RegionHandshake packet");
189 System.Text.Encoding _enc = System.Text.Encoding.ASCII;
190 RegionHandshakePacket handshake = new RegionHandshakePacket();
191
192 Console.WriteLine("Avatar.cs:SendRegionhandshake() - Filling in RegionHandshake details");
193 handshake.RegionInfo.BillableFactor = 0;
194 handshake.RegionInfo.IsEstateManager = false;
195 handshake.RegionInfo.TerrainHeightRange00 = 60;
196 handshake.RegionInfo.TerrainHeightRange01 = 60;
197 handshake.RegionInfo.TerrainHeightRange10 = 60;
198 handshake.RegionInfo.TerrainHeightRange11 = 60;
199 handshake.RegionInfo.TerrainStartHeight00 = 10;
200 handshake.RegionInfo.TerrainStartHeight01 = 10;
201 handshake.RegionInfo.TerrainStartHeight10 = 10;
202 handshake.RegionInfo.TerrainStartHeight11 = 10;
203 handshake.RegionInfo.SimAccess = 13;
204 handshake.RegionInfo.WaterHeight = 5;
205 handshake.RegionInfo.RegionFlags = 72458694;
206 handshake.RegionInfo.SimName = _enc.GetBytes(OpenSim_Main.cfg.RegionName + "\0");
207 handshake.RegionInfo.SimOwner = new LLUUID("00000000-0000-0000-0000-000000000000");
208 handshake.RegionInfo.TerrainBase0 = new LLUUID("b8d3965a-ad78-bf43-699b-bff8eca6c975");
209 handshake.RegionInfo.TerrainBase1 = new LLUUID("abb783e6-3e93-26c0-248a-247666855da3");
210 handshake.RegionInfo.TerrainBase2 = new LLUUID("179cdabd-398a-9b6b-1391-4dc333ba321f");
211 handshake.RegionInfo.TerrainBase3 = new LLUUID("beb169c7-11ea-fff2-efe5-0f24dc881df2");
212 handshake.RegionInfo.TerrainDetail0 = new LLUUID("00000000-0000-0000-0000-000000000000");
213 handshake.RegionInfo.TerrainDetail1 = new LLUUID("00000000-0000-0000-0000-000000000000");
214 handshake.RegionInfo.TerrainDetail2 = new LLUUID("00000000-0000-0000-0000-000000000000");
215 handshake.RegionInfo.TerrainDetail3 = new LLUUID("00000000-0000-0000-0000-000000000000");
216 handshake.RegionInfo.CacheID = new LLUUID("545ec0a5-5751-1026-8a0b-216e38a7ab37");
217
218 Console.WriteLine("Avatar.cs:SendRegionHandshake() - Sending RegionHandshake packet");
219 this.ControllingClient.OutPacket(handshake);
220 }
221 }
222
223 public class NewForce
224 {
225 public float X;
226 public float Y;
227 public float Z;
228
229 public NewForce()
230 {
231
232 }
150 } 233 }
151} 234}
diff --git a/src/world/Entity.cs b/src/world/Entity.cs
index ab07fd6..72e4e83 100644
--- a/src/world/Entity.cs
+++ b/src/world/Entity.cs
@@ -9,9 +9,9 @@ namespace OpenSim.world
9 public class Entity 9 public class Entity
10 { 10 {
11 protected libsecondlife.LLUUID uuid; 11 protected libsecondlife.LLUUID uuid;
12 protected Vector3 position; 12 public Vector3 position;
13 protected Vector3 velocity; 13 public Vector3 velocity;
14 protected Quaternion rotation; 14 public Quaternion rotation;
15 protected string name; 15 protected string name;
16 protected List<Entity> children; 16 protected List<Entity> children;
17 17
@@ -24,7 +24,13 @@ namespace OpenSim.world
24 name = "(basic entity)"; 24 name = "(basic entity)";
25 children = new List<Entity>(); 25 children = new List<Entity>();
26 } 26 }
27 27 public virtual void addFroces()
28 {
29 foreach (Entity child in children)
30 {
31 child.addFroces();
32 }
33 }
28 public virtual void update() { 34 public virtual void update() {
29 // Do any per-frame updates needed that are applicable to every type of entity 35 // Do any per-frame updates needed that are applicable to every type of entity
30 foreach (Entity child in children) 36 foreach (Entity child in children)
diff --git a/src/world/World.cs b/src/world/World.cs
index ebbd61e..920220f 100644
--- a/src/world/World.cs
+++ b/src/world/World.cs
@@ -3,6 +3,7 @@ using libsecondlife;
3using libsecondlife.Packets; 3using libsecondlife.Packets;
4using System.Collections.Generic; 4using System.Collections.Generic;
5using System.Text; 5using System.Text;
6using PhysicsManager;
6 7
7namespace OpenSim.world 8namespace OpenSim.world
8{ 9{
@@ -13,6 +14,8 @@ namespace OpenSim.world
13 public ScriptEngine Scripts; 14 public ScriptEngine Scripts;
14 public TerrainDecode terrainengine = new TerrainDecode(); 15 public TerrainDecode terrainengine = new TerrainDecode();
15 public uint _localNumber=0; 16 public uint _localNumber=0;
17 private PhysicsScene phyScene;
18 private float timeStep= 0.1f;
16 19
17 private Random Rand = new Random(); 20 private Random Rand = new Random();
18 21
@@ -32,9 +35,30 @@ namespace OpenSim.world
32 // Initialise this only after the world has loaded 35 // Initialise this only after the world has loaded
33 Scripts = new ScriptEngine(this); 36 Scripts = new ScriptEngine(this);
34 } 37 }
35 38
39 public PhysicsScene PhysScene
40 {
41 set
42 {
43 this.phyScene = value;
44 }
45 }
46
36 public void Update() 47 public void Update()
37 { 48 {
49 if(this.phyScene.IsThreaded)
50 {
51 this.phyScene.GetResults();
52
53 }
54
55 foreach (libsecondlife.LLUUID UUID in Entities.Keys)
56 {
57 Entities[UUID].addFroces();
58 }
59
60 this.phyScene.Simulate(timeStep);
61
38 foreach (libsecondlife.LLUUID UUID in Entities.Keys) 62 foreach (libsecondlife.LLUUID UUID in Entities.Keys)
39 { 63 {
40 Entities[UUID].update(); 64 Entities[UUID].update();
@@ -55,7 +79,9 @@ namespace OpenSim.world
55 this.Entities.Add(AgentClient.AgentID, NewAvatar); 79 this.Entities.Add(AgentClient.AgentID, NewAvatar);
56 Console.WriteLine("World.cs:AddViewerAgent() - Starting RegionHandshake "); 80 Console.WriteLine("World.cs:AddViewerAgent() - Starting RegionHandshake ");
57 NewAvatar.SendRegionHandshake(this); 81 NewAvatar.SendRegionHandshake(this);
58 this.Update(); // will work for now, but needs to be optimised so we don't update everything in the sim for each new user 82
83 NewAvatar.PhysActor = this.phyScene.AddAvatar(new PhysicsVector(NewAvatar.position.x, NewAvatar.position.y, NewAvatar.position.z));
84 //this.Update(); // will work for now, but needs to be optimised so we don't update everything in the sim for each new user
59 } 85 }
60 86
61 public bool Backup() { 87 public bool Backup() {