/*
* Copyright (c) 2001 Silvere Martin-Michiellot All Rights Reserved.
*
* Silvere Martin-Michiellot grants you ("Licensee") a non-exclusive,
* royalty free, license to use, modify and redistribute this
* software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Silvere Martin-Michiellot.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. Silvere Martin-Michiellot
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
* Silvere Martin-Michiellot OR ITS LICENSORS BE LIABLE
* FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF Silvere Martin-Michiellot HAS BEEN
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*
* @Author: Silvere Martin-Michiellot
*
*/
package com.db.server;
import java.lang.IllegalArgumentException;
import java.security.PublicKey;
import java.util.*;
import javax.jdo.*;
import javax.media.j3d.Bounds;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Transform3D;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import com.db.utils.tree.*;
/**
*/
//XXXXX
//track Avatar addition and deletion with Filters
public class Avatar extends Object {
public final static int IMMIGRATING = 1;
public final static int NORMAL = 2;
public final static int AWAY = 3;
public final static int PAWN = 4;
//NONE stands for no relation between two avatars (which are therefore not on the same worldSpot)
public final static int NONE = 0;
public final static int RULER = 1;
public final static int LORD = 2;
public final static int PEERSLORD = 4;
public final static int PEER = 8;
public final static int PEERSVASSAL = 16;
public final static int VASSAL = 32;
public final static int SELF = 64;
public final static int LAST_AVATAR_KIND = (Avatar.SELF * 2) - 1;
public final static int HIDDEN = 0;
public final static int READ = 1;
public final static int READ_WRITE = 2;
//succession policies
public final static int GIVE_ALL_TO_LORD = 0;
public final static int SHARE_AMONG_PEER = 1;
public final static int SHARE_AMONG_DIRECT_VASSALS = 2;
public final static int SHARE_AMONG_VASSALS = 3;
public final static int TRASH_ALL = 4;
public final static int DEFAULT_SUCCESSION_POLICY = Avatar.GIVE_ALL_TO_LORD;
/**
* owner login (unique for a given Universe, shared between World Spots)
* owner password
* owner public key (send to server and other clients)
* certificates (with their validity: duration, sender, receiver, eligible rights)
* tickets (owner's proof of purchase of different objects or tools) (id of transaction, object, price, date, seller public key) and receipts (the counterpart of the ticket that goes to the seller) (id of transaction, object, price, date, buyer public key)
* status (internal) either immigrating, normal, away (not connected or in another world spot or virtual world) or pawn (login, password, public key, certificates, tickets and receipts should be deleted as well as textual avatar information) (position and orientation should remain the same)
* (tools should be designed to give the opportunity to the lord of the user to dispatch lands, vassals pawns, objects, tools, guides and even geometry and sound description of the user so that the account is really deleted) (when everything has been dispatched the avatar should be automatically removed from the server)
* vassals (if any) (this is only a pointer to a query on the world spot for convenience)
* pawns (virtual vassals) used when the vassal is removed but its belongings are not explicitely transfered to the user (when the vassal account is closed, but not deleted it is transmuted into a pawn) (pawns are not guides)
* owner's lands (if any) (this is only a pointer to a query on the world spot for convenience)
* owner's home position and orientation (if any) (must be in lands boundary) (the place where objects will try to fall if not occupied by something else when an object is imported, bought or rejected from a certain land) (if an object can't go to home, it should be stored in the inventory or action should be refused)
* "Geometry description": (subclass of 3D Object)
* owner's current position and orientation
* skin (joint and segments replicating the H-Anim specification)
* accessories (visible and audible tools worn on the body but accessible from the inventory) and their relative positions and orientations on the skin.
* verbals (words that can be pronounced by the avatar's skin mouth as skin deformation and sound emission)
* movements (overall body movements)
* gestures (particular joint and segment combination movements without global movement)
* Sound description (sounds that correspond to movements or sound of voice) (sounds from accessories are managed by the accessory itself) (subclass of 3D Object)
* Capabilities (4 booleans grant access for invited lords, peers, peers vassals and vassals) (ancestors of a user can always collide, see and hear a user if they have requested so):
* collidable
* visible
* sonore
* hands (automatically accept to shake hands, but still requires that another user that wants to shake bounces into the avatar)
* textual avatar information (how the user self perceives its avatar, name, firstame, sex, age, e-mail, address, phone in an hashtable with empty fields)
* inventory (the pocket of the user in which to store collected objects, useful tools and guides)
* notification filter
**/
private final static String STRING_NAME = "Name";
private final static String STRING_FIRSTNAME = "Firstname";
private final static String STRING_SEX = "Sex";
private final static String STRING_AGE = "Age";
private final static String STRING_E_MAIL = "e-mail";
private final static String STRING_ADDRESS = "Address";
private final static String STRING_PHONE = "Phone";
private UniverseServer universeServer;
private LoginInformation loginInformation;
private Hashtable information;
private int status;
private HashSet vassals;
private Transform3D homePosition;
private Skin skin;
private HashSet inventory;
private int landSuccessionPolicy;
private int objectWorldSuccessionPolicy;
private int backgroundWorldSuccessionPolicy;
//In a big community, lords will probably be notified about many vassals actions (requests from their vassals to access land...). This should rapidly create an
//unwanted network overload as well as a lot of unwanted junk data. A filter mechanism is proposed in which users can decide not to be notified when a
//vassal makes a request (the notification message is not send from the server). The filter is a list of pairs key-key value.
//The key mentions the Object concerned and the kind of request:
// Request field read - field write (of an object, tool, guide, avatar, land, World Spot)
// Request action execution (of an object, tool, guide, avatar, land, World Spot), the action may involve creation, modification or deletion of an object,
// tool, guide, avatar, land.
//The key-value is given with 6 booleans to grant notification about users' actions for the owner himself, lords, invited lords, peers, peers vassals and vassals.
//Using the notification filter, users can choose not to see or hear or collide from objects, tools, guides, avatars, owners (corresponding fields in the avatar).
//Users that make no filtering will have a log of every actions about themselves, lords, invited lords, peers, peers vassals and vassals. Since lords can also
//decide to refuse to be queried, this log can be incomplete. The only complete log is the one of the top lord if he requests no filtering.
//
//the notification filter mentions positive notification
//users are notified of nothing (except answers to their requests) if they do not request anything
//if a user want to be notified of a lord visiting one of its lands then he has to add en entry in the notification filter
//When a user doesn't want some automatic requests he sends to be completed
//For example if he doesn't want to hear from someone
//when a user wants to create a new account, all its parent must have vassalsMayCreateAccounts set to true;
private boolean vassalsMayCreateAccounts;
private Avatar() {
throw new IllegalArgumentException("An avatar must have a universeServer and a loginInformation.");
}
public Avatar(UniverseServer universeServer, LoginInformation loginInformation) {
this.loginInformation = loginInformation;
this.universeServer = universeServer;
this.information = new Hashtable();
this.status = Avatar.IMMIGRATING;
this.vassals = new HashSet();
this.inventory = new HashSet();
this.vassalsMayCreateAccounts = true;
this.landSuccessionPolicy = DEFAULT_SUCCESSION_POLICY;
this.objectWorldSuccessionPolicy = DEFAULT_SUCCESSION_POLICY;
this.backgroundWorldSuccessionPolicy = DEFAULT_SUCCESSION_POLICY;
}
public final LoginInformation getLoginInformation() {
return loginInformation;
}
public final UniverseServer getUniverseServer() {
return this.universeServer;
}
private final PersistenceManager getPersistenceManager() {
return this.universeServer.getPersistenceManager();
}
// avatar name, firstame, sex, age, e-mail, address, phone
public final Hashtable getInformation() {
return information;
}
public final String getInformation(String key) {
return (String)information.get(key);
}
public final void setLoginInformation(LoginInformation loginInformation) {
if (this.loginInformation.checkLoginAndPassword(loginInformation)) {
this.loginInformation = loginInformation;
}
}
// be cautious with this method as it obliterates the avatar name, firstame, sex, age, e-mail, address, phone
//obliterates every previously stored information including reserved fields
public final void setInformation(Hashtable information) {
this.information = information;
}
//use the corresponding method to set the individual default fields
public final void addInformation(String key, String keyValue) {
//check if it is a reserved field or not
if ((key!=Avatar.STRING_NAME) && (key!=Avatar.STRING_FIRSTNAME) && (key!=Avatar.STRING_SEX) && (key!=Avatar.STRING_AGE) && (key!=Avatar.STRING_E_MAIL) && (key!=Avatar.STRING_ADDRESS) && (key!=Avatar.STRING_PHONE)) {
this.information.put(key, keyValue);
}
else {
throw new IllegalArgumentException("You can't add directely Information on the restricted fields.");
}
}
public final void removeInformation(String key) {
//check if it is a reserved field or not
if ((key!=Avatar.STRING_NAME) && (key!=Avatar.STRING_FIRSTNAME) && (key!=Avatar.STRING_SEX) && (key!=Avatar.STRING_AGE) && (key!=Avatar.STRING_E_MAIL) && (key!=Avatar.STRING_ADDRESS) && (key!=Avatar.STRING_PHONE)) {
this.information.remove(key);
}
else {
throw new IllegalArgumentException("You can't remove directely Information on the restricted fields.");
}
}
//uses the key Name
public String getAvatarName() {
return this.getInformation(Avatar.STRING_NAME);
}
//uses the key Name
public void setAvatarName(String name) {
this.addInformation(Avatar.STRING_NAME, name);
}
//uses the key Firstname
public String getAvatarFirstname() {
return this.getInformation(Avatar.STRING_FIRSTNAME);
}
//uses the key Firstname
public void setAvatarFirstname(String firstname) {
this.addInformation(Avatar.STRING_FIRSTNAME, firstname);
}
//uses the key Sex (should be either male, female, other)
public String getAvatarSex() {
return this.getInformation(Avatar.STRING_SEX);
}
//uses the key Sex (should be either male, female, other)
public void setAvatarSex(String sex) {
this.addInformation(Avatar.STRING_SEX, sex);
}
//uses the key Age (this is a String with numerical caracters only)
public String getAvatarAge() {
return this.getInformation(Avatar.STRING_AGE);
}
//uses the key Age (this is a String with numerical caracters only)
public void setAvatarAge(String age) {
this.addInformation(Avatar.STRING_AGE, age);
}
//uses the key EMail
public String getAvatarEMail() {
return this.getInformation(Avatar.STRING_E_MAIL);
}
//uses the key EMail
public void setAvatarEMail(String eMail) {
this.addInformation(Avatar.STRING_E_MAIL, eMail);
}
//uses the key Address (real world address, virtual home is given with access to home position)
public String getAvatarAddress() {
return this.getInformation(Avatar.STRING_ADDRESS);
}
//uses the key Address (real world address, virtual home is given with access to home position)
public void setAvatarAddress(String address) {
this.addInformation(Avatar.STRING_ADDRESS, address);
}
//uses the key Phone (real phone number, mostly useless)
public String getAvatarPhone() {
return this.getInformation(Avatar.STRING_PHONE);
}
//uses the key Phone (real phone number, mostly useless)
public void setAvatarPhone(String phone) {
this.addInformation(Avatar.STRING_PHONE, phone);
}
public final Collection getPass() {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
refreshPass();
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Pass.class, true);
filter = new String("SELECT Pass FROM Pass p WHERE p.getReceiver()==this");
query = this.getPersistenceManager().newQuery(Pass.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
return collection;
}
catch (Exception exception) {
return null;
}
}
//pass is not plural here
//does automatically add pass to receiver of the pass
public final void addPass(Pass pass) {
Transaction transaction;
refreshPass();
//may be we should check Pass does not already exist
if (pass!=null) {
if ((pass.getSender() == this) || (pass.getReceiver() == this)) {
if ((pass.getEndDate()>this.getUniverseServer().getServerTime()) && (pass.getStartDate()>universeServer.getServerTime())) {
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
this.getPersistenceManager().makePersistent(pass);
transaction.commit();
}
catch (Exception exception) {
}
}
}
}
}
//pass is not plural here
//does automatically remove pass to sender of the pass
//you remove pass you received
public final void removePass(Pass pass) {
Transaction transaction;
refreshPass();
if (pass.getReceiver()==this) {
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
this.getPersistenceManager().deletePersistent(pass);
transaction.commit();
}
catch (Exception exception) {
}
}
}
private void removeAllPass() {
Iterator iterator;
iterator = this.getPass().iterator();
while (iterator.hasNext()) {
this.removePass((Pass)iterator.next());
}
}
private void refreshPass() {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Pass.class, true);
filter = new String("DELETE Pass FROM Pass p WHERE p.getReceiver()==this AND p.getEndDate()<=this.getUniverseServer().getServerTime()");
query = this.getPersistenceManager().newQuery(Pass.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
}
catch (Exception exception) {
}
}
public final Collection getTicketsOrReceipts() {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Ticket.class, true);
filter = new String("SELECT Ticket FROM Ticket t WHERE t.getSellerOrBuyer()==this");
query = this.getPersistenceManager().newQuery(Ticket.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
return collection;
}
catch (Exception exception) {
return null;
}
}
//because method is protected we do not check if Ticket is emited or received by this avatar which should always be the case
protected final void addTicketOrReceipt(Ticket ticketOrReceipt) {
Transaction transaction;
//may be we should check Ticket does not already exist
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
this.getPersistenceManager().makePersistent(ticketOrReceipt);
transaction.commit();
}
catch (Exception exception) {
}
}
public final void removeTicketOrReceipt(Ticket ticketOrReceipt) {
Transaction transaction;
if (ticketOrReceipt.getSellerOrBuyer()==this) {
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
this.getPersistenceManager().deletePersistent(ticketOrReceipt);
transaction.commit();
}
catch (Exception exception) {
}
}
}
private void removeAllTicketOrReceipts() {
Iterator iterator;
iterator = this.getTicketsOrReceipts().iterator();
while (iterator.hasNext()) {
this.removeTicketOrReceipt((Ticket)iterator.next());
}
}
public final int getStatus() {
return this.status;
}
//calls only valid by the system
//client calls are useless
//valid values range for
//Avatar.IMMIGRATING; (when moving between worldspots or moving between gates) (not first connection) (may be used to display special information on arrival) (this status should not last and change automatically to Avatar.NORMAL)
//Avatar.NORMAL;
//Avatar.AWAY;
//Avatar.PAWN;
//Once a pawn, an avatar can't have it's status restored back
//this is in other words the way accounts are closed
public final void setStatus(int status) {
HashSet hashSet;
Iterator iterator;
Avatar avatar;
Transaction transaction;
if ((status>=Avatar.IMMIGRATING) && (status<=Avatar.PAWN)) {
if (this.status!=Avatar.PAWN) {
if (status==Avatar.PAWN) {
pawnUser();
}
else {
if ((this.status!=Avatar.AWAY) && (status==Avatar.AWAY)) {
//Warn server to close connection
this.getUniverseServer().closeConnection(this);
}
this.status = status;
}
}
}
}
//gets succession policies in a 3 int array that provides landPolicy, objectWorldPolicy, backgroundWorldPolicy in that order
public final int[] getSuccessionPolicies() {
int[] policies;
policies = new int[3];
policies[0]= this.landSuccessionPolicy;
policies[1]= this.objectWorldSuccessionPolicy;
policies[2]= this.backgroundWorldSuccessionPolicy;
return policies;
}
//can only be called by self
//landPolicy.TRASH_ALL is forbidden for this implementation (makes holes in the worldspot)
public final void setSuccessionPolicy(int landPolicy, int objectWorldPolicy, int backgroundWorldPolicy) {
if ((landPolicy>=GIVE_ALL_TO_LORD) && (landPolicy<=SHARE_AMONG_VASSALS)) {
this.landSuccessionPolicy = landPolicy;
}
if ((objectWorldPolicy>=GIVE_ALL_TO_LORD) && (objectWorldPolicy<=TRASH_ALL)) {
this.objectWorldSuccessionPolicy = objectWorldPolicy;
}
if ((backgroundWorldPolicy>=GIVE_ALL_TO_LORD) && (backgroundWorldPolicy<=TRASH_ALL)) {
this.backgroundWorldSuccessionPolicy = backgroundWorldPolicy;
}
}
//closes client-server connection: this is a logout (or a kick-out)
public final void disconnectUser() {
this.setStatus(Avatar.AWAY);
}
//user is automatically deleted when the Pawn belongings have all been transfered or deleted
//hierarchy may therefore sometimes collapse and users should be notified (their vassals should be updated)
//equivalent to this.setStatus(Avatar.PAWN);
//can't pawn ruler
//a pool is opened in which all belongings are put for 60 days depending of succession policy
//uses sytem time not universeServer time (offset difference)
//Trashing land is a bas idea since it makes holes in the worldspot and is therefore forbidden for this implementation
public final void pawnUser() {
Avatar lord;
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
Iterator iterator;
boolean poolExists;
Pool pool;
Date date;
Avatar avatar;
VirtualElement virtualElement;
Bounds bounds;
lord = Avatar.getLord(this);
//can't pawn ruler
if (lord!=null) {
if (this.status!=Avatar.PAWN) {
synchronized (this) {
this.disconnectUser();
this.status=Avatar.PAWN;
//throw everything out of inventory
this.removeAllInventoryEntries();
poolExists = false;
pool = null;
//get VirtualElements: Land
collection = this.getLands();
//do something of VirtualElements
if (this.landSuccessionPolicy == Avatar.GIVE_ALL_TO_LORD) {
this.removeAllLands();
} else {
if (this.landSuccessionPolicy == Avatar.SHARE_AMONG_PEER) {
pool = Avatar.getWorldSpot(this).startPool(this);
poolExists = true;
iterator = collection.iterator();
date = new Date(System.currentTimeMillis() + 1000*3600*24*60);
while (iterator.hasNext()) {
virtualElement = (VirtualElement) iterator.next();
pool.dontSellUnder(this, virtualElement, date);
}
iterator = lord.getVassals().iterator();
while (iterator.hasNext()) {
avatar = (Avatar) iterator.next();
pool.comeIntoPool(avatar);
}
} else {
if (this.landSuccessionPolicy == Avatar.SHARE_AMONG_DIRECT_VASSALS) {
pool = Avatar.getWorldSpot(this).startPool(this);
poolExists = true;
iterator = collection.iterator();
date = new Date(System.currentTimeMillis() + 1000*3600*24*60);
while (iterator.hasNext()) {
virtualElement = (VirtualElement) iterator.next();
pool.dontSellUnder(this, virtualElement, date);
}
iterator = this.getVassals().iterator();
while (iterator.hasNext()) {
avatar = (Avatar) iterator.next();
pool.comeIntoPool(avatar);
}
} else {
if (this.landSuccessionPolicy == Avatar.SHARE_AMONG_VASSALS) {
pool = Avatar.getWorldSpot(this).startPool(this);
poolExists = true;
iterator = collection.iterator();
date = new Date(System.currentTimeMillis() + 1000*3600*24*60);
while (iterator.hasNext()) {
virtualElement = (VirtualElement) iterator.next();
pool.dontSellUnder(this, virtualElement, date);
}
iterator = this.getAllVassals().iterator();
while (iterator.hasNext()) {
avatar = (Avatar) iterator.next();
pool.comeIntoPool(avatar);
}
} else {
//Avatar.TRASH_ALL
//we actually do not trash all lands because this would create a hole in the worldspot
this.removeAllLands();
}
}
}
}
//get VirtualElements: ObjectWorld
collection = this.getObjectWorlds();
//do something of VirtualElements
if (this.objectWorldSuccessionPolicy == Avatar.GIVE_ALL_TO_LORD) {
Avatar.getWorldSpot(this).getTrade().exchange(this, (VirtualElement[])collection.toArray(), lord, new VirtualElement[0]);
} else {
if (this.objectWorldSuccessionPolicy == Avatar.SHARE_AMONG_PEER) {
if (!poolExists) {
pool = Avatar.getWorldSpot(this).startPool(this);
}
iterator = collection.iterator();
date = new Date(System.currentTimeMillis() + 1000*3600*24*60);
while (iterator.hasNext()) {
virtualElement = (VirtualElement) iterator.next();
pool.dontSellUnder(this, virtualElement, date);
}
iterator = lord.getVassals().iterator();
while (iterator.hasNext()) {
avatar = (Avatar) iterator.next();
pool.comeIntoPool(avatar);
}
} else {
if (this.objectWorldSuccessionPolicy == Avatar.SHARE_AMONG_DIRECT_VASSALS) {
if (!poolExists) {
pool = Avatar.getWorldSpot(this).startPool(this);
}
iterator = collection.iterator();
date = new Date(System.currentTimeMillis() + 1000*3600*24*60);
while (iterator.hasNext()) {
virtualElement = (VirtualElement) iterator.next();
pool.dontSellUnder(this, virtualElement, date);
}
iterator = this.getVassals().iterator();
while (iterator.hasNext()) {
avatar = (Avatar) iterator.next();
pool.comeIntoPool(avatar);
}
} else {
if (this.objectWorldSuccessionPolicy == Avatar.SHARE_AMONG_VASSALS) {
if (!poolExists) {
pool = Avatar.getWorldSpot(this).startPool(this);
}
iterator = collection.iterator();
date = new Date(System.currentTimeMillis() + 1000*3600*24*60);
while (iterator.hasNext()) {
virtualElement = (VirtualElement) iterator.next();
pool.dontSellUnder(this, virtualElement, date);
}
iterator = this.getAllVassals().iterator();
while (iterator.hasNext()) {
avatar = (Avatar) iterator.next();
pool.comeIntoPool(avatar);
}
} else {
//Avatar.TRASH_ALL
iterator = collection.iterator();
while (iterator.hasNext()) {
virtualElement = (VirtualElement)iterator.next();
virtualElement.deleteVirtualElement(this, virtualElement);
}
}
}
}
}
//get VirtualElements: BackgroundWorld
//do something of VirtualElements
if (this.backgroundWorldSuccessionPolicy == Avatar.GIVE_ALL_TO_LORD) {
this.removeAllBackgroundWorlds();
} else {
collection = this.getBackgroundWorlds();
if (this.backgroundWorldSuccessionPolicy == Avatar.SHARE_AMONG_PEER) {
if (!poolExists) {
pool = Avatar.getWorldSpot(this).startPool(this);
}
iterator = collection.iterator();
date = new Date(System.currentTimeMillis() + 1000*3600*24*60);
while (iterator.hasNext()) {
virtualElement = (VirtualElement) iterator.next();
pool.dontSellUnder(this, virtualElement, date);
}
iterator = lord.getVassals().iterator();
while (iterator.hasNext()) {
avatar = (Avatar) iterator.next();
pool.comeIntoPool(avatar);
}
} else {
if (this.backgroundWorldSuccessionPolicy == Avatar.SHARE_AMONG_DIRECT_VASSALS) {
if (!poolExists) {
pool = Avatar.getWorldSpot(this).startPool(this);
}
iterator = collection.iterator();
date = new Date(System.currentTimeMillis() + 1000*3600*24*60);
while (iterator.hasNext()) {
virtualElement = (VirtualElement) iterator.next();
pool.dontSellUnder(this, virtualElement, date);
}
iterator = this.getVassals().iterator();
while (iterator.hasNext()) {
avatar = (Avatar) iterator.next();
pool.comeIntoPool(avatar);
}
} else {
if (this.backgroundWorldSuccessionPolicy == Avatar.SHARE_AMONG_VASSALS) {
if (!poolExists) {
pool = Avatar.getWorldSpot(this).startPool(this);
}
iterator = collection.iterator();
date = new Date(System.currentTimeMillis() + 1000*3600*24*60);
while (iterator.hasNext()) {
virtualElement = (VirtualElement) iterator.next();
pool.dontSellUnder(this, virtualElement, date);
}
iterator = this.getAllVassals().iterator();
while (iterator.hasNext()) {
avatar = (Avatar) iterator.next();
pool.comeIntoPool(avatar);
}
} else {
//Avatar.TRASH_ALL
iterator = collection.iterator();
while (iterator.hasNext()) {
virtualElement = (VirtualElement)iterator.next();
virtualElement.deleteVirtualElement(this, virtualElement);
}
}
}
}
}
//we have to remove some fields and notify parent
//(login, password, public key, certificates, tickets and receipts should be deleted as well as textual avatar information)
//(position and orientation should remain the same)
this.loginInformation = null;
this.setInformation(null);
//pass should remain valid
//tickets and receipts are deleted as the last step of avatar deletion
this.removeAllBlockers();
this.removeAllFilters();
//database should refresh this
}
}
}
}
//some avatars may not have a client
//if their status is immigrating, away or pawn
public boolean avatarHasClient() {
return (this.status==Avatar.NORMAL);
}
//every vassal whether pawn of not
public final HashSet getVassals() {
return this.vassals;
}
//further calls about vassals, pawns and lands are actually queries to the database
//these are pointers to real objects stored in the World Spot
//vassal is reParented to be a vassal of avatar
//this is equivalent to Avatar.moveUserInHierarchy(vassal, this, Avatar.getWorldsSpot(this).getRuler());
protected final void addVassal(Avatar vassal) {
this.moveUserInHierarchy(vassal, this);
}
public final void addVassal(Avatar vassal, Avatar agreeingParent) {
this.moveUserInHierarchy(vassal, this, agreeingParent);
}
//this call is equivalent to: Avatar.moveUserInHierarchy(vassal, vassal.getLord().getLord(), vassal.getLord().getLord());
//receiving Lord should be able to accept parent
//vassal must be a vassal of this avatar and avatar mustn't be a ruler
protected final void removeVassal(Avatar vassal) {
if ((!isRuler(this)) && (this.vassals.contains(vassal))) {
this.moveUserInHierarchy(vassal, Avatar.getLord(Avatar.getLord(vassal)));
}
}
public final void removeVassal(Avatar vassal, Avatar agreeingParent) {
if ((!isRuler(this)) && (this.vassals.contains(vassal))) {
this.moveUserInHierarchy(vassal, Avatar.getLord(Avatar.getLord(vassal)), agreeingParent);
}
}
private final void createVassal(Avatar vassal) {
//since this is a private method, avatar is believed to exist and not be a vassal of any avatar
this.vassals.add(vassal);
}
private final void killVassal(Avatar vassal) {
//since this is a private method, avatar is believed to exist and be a vassal of this
this.vassals.remove(vassal);
}
//only pawn vassals
public final HashSet getPawns() {
HashSet hashSet;
Iterator iterator;
Avatar avatar;
hashSet = new HashSet();
iterator = this.vassals.iterator();
while (iterator.hasNext()) {
avatar =(Avatar) iterator.next();
if (avatar.getStatus()==Avatar.PAWN) {
hashSet.add(avatar);
}
}
return hashSet;
}
public final Collection getObjectWorlds() {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(ObjectWorld.class, true);
filter = new String("SELECT ObjectWorld FROM ObjectWorld o WHERE o.getUserOwner()==this");
query = this.getPersistenceManager().newQuery(ObjectWorld.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
return collection;
}
catch (Exception exception) {
return null;
}
}
public final Collection getLands() {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Land.class, true);
filter = new String("SELECT Land FROM Land l WHERE l.getUserOwner()==this");
query = this.getPersistenceManager().newQuery(Land.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
return collection;
}
catch (Exception exception) {
return null;
}
}
//land ownership is transfered to this avatar
protected final void addLand(Land land) {
Land[] lands;
if ((land!=null) && (land.getUserOwner()!=this)) {
lands = new Land[1];
lands[0] = land;
Avatar.getWorldSpot(this).getTrade().exchange(land.getUserOwner(), lands, this, null);
}
}
//land is reparented to this.getLord() if possible
public final void removeLand(Land land) {
Avatar avatar;
Land[] lands;
avatar = Avatar.getLord(this);
if ((avatar!=null) && (land!=null) && (land.getUserOwner()==this)) {
lands = new Land[1];
lands[0] = land;
Avatar.getWorldSpot(this).getTrade().exchange(this, lands, avatar, null);
}
}
//not valid for a ruler
private final void removeAllLands() {
Avatar.getWorldSpot(this).getTrade().exchange(this, (VirtualElement[])this.getLands().toArray(), Avatar.getLord(this), new VirtualElement[0]);
}
public final Collection getBackgroundWorlds() {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(BackgroundWorld.class, true);
filter = new String("SELECT BackgroundWorld FROM BackgroundWorld b WHERE b.getUserOwner()==this");
query = this.getPersistenceManager().newQuery(BackgroundWorld.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
return collection;
}
catch (Exception exception) {
return null;
}
}
//backgoundWorld ownership is transfered to this avatar
protected final void addBackgoundWorld(BackgroundWorld backgroundWorld) {
BackgroundWorld[] backgroundWorlds;
if ((backgroundWorld!=null) && (backgroundWorld.getUserOwner()!=this)) {
backgroundWorlds = new BackgroundWorld[1];
backgroundWorlds[0] = backgroundWorld;
Avatar.getWorldSpot(this).getTrade().exchange(backgroundWorld.getUserOwner(), backgroundWorlds, this, null);
}
}
//backgroundWorld is reparented to this.getLord() if possible
public final void removeBackgroundWorld(BackgroundWorld backgroundWorld) {
Avatar avatar;
BackgroundWorld[] backgroundWorlds;
avatar = Avatar.getLord(this);
if ((avatar!=null) && (backgroundWorld!=null) && (backgroundWorld.getUserOwner()==this)) {
backgroundWorlds = new BackgroundWorld[1];
backgroundWorlds[0] = backgroundWorld;
Avatar.getWorldSpot(this).getTrade().exchange(this, backgroundWorlds, avatar, null);
}
}
//not valid for a ruler
private final void removeAllBackgroundWorlds() {
Avatar.getWorldSpot(this).getTrade().exchange(this, (VirtualElement[])this.getBackgroundWorlds().toArray(), Avatar.getLord(this), new VirtualElement[0]);
}
public final Transform3D getHomePosition() {
return this.homePosition;
}
public final void setHomePosition(Transform3D homePosition) {
if (this.checkHome(homePosition)) {
this.homePosition = homePosition;
}
}
//sets a home using a land if one exists eventually overwriting the existing home position
protected final void createHome() {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
Land land;
Point3d center;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Land.class, true);
filter = new String("SELECT Land FROM Land l WHERE l.getUserOwner()==this");
query = this.getPersistenceManager().newQuery(Land.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
if (collection.size()>0) {
//collection
land = (Land) collection.iterator().next();
this.homePosition = new Transform3D();
center = new Point3d();
new BoundingSphere(land.getBounds()).getCenter(center);
this.homePosition.set(new Vector3d(center));
}
}
catch (Exception exception) {
}
}
private final boolean checkHome(Transform3D homePosition) {
Transform3D oldTransform3D;
Point3d point3d;
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
HashSet result;
point3d = new Point3d();
homePosition.transform(point3d);
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Land.class, true);
filter = new String("SELECT Land FROM Land l WHERE l.getBounds().intersect(point3d)");
query = this.getPersistenceManager().newQuery(Land.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
result = this.filterCollection(this, collection);
return (result.size()>0);
}
catch (Exception exception) {
return false;
}
}
public final Skin getSkin() {
return this.skin;
}
//skin owner should be the same as this owner
public final void setSkin(Skin skin) {
if ((skin !=null) && (skin.getUserOwner()==this)) {
this.skin = skin;
}
}
public final HashSet getInventory() {
return this.inventory;
}
//objectWorld should not be bound to another user's inventory but this is checked
//can add weared skins but it is not recommended because some avatar is going to be invisible
//object must be owned by user
//capabilities must be set to ObjectWorld.VISIBLE
//capabilities must be set to ObjectWorld.MOVABLE, ObjectWorld.SONORE
//capabilities must be set to ObjectWorld.INVENTORIZABLE (system wide check)
public final void addInventoryEntry(ObjectWorld objectWorld) {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
if ((objectWorld!=null) && (objectWorld.getUserOwner()==this)) {
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Avatar.class, true);
filter = new String("SELECT Avatar FROM Avatar a WHERE a.getInventory().contains(objectWorld)");
query = this.getPersistenceManager().newQuery(Avatar.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
//there should be at most one avatar
if (collection.size()==0) {
if (objectWorld.checkWritable(ObjectWorld.INVENTORIZABLE + ObjectWorld.VISIBLE + ObjectWorld.SONORE + ObjectWorld.MOVABLE)) {
this.inventory.add(objectWorld);
objectWorld.setInvisible();
objectWorld.setUnSonore();
objectWorld.doInventorize(this);
}
}
}
catch (Exception exception) {
}
}
}
private void removeAllInventoryEntries() {
Iterator iterator;
iterator = this.inventory.iterator();
while (iterator.hasNext()) {
this.removeInventoryEntry((ObjectWorld)iterator.next());
}
}
//objectWorld should pop out of user's pocket where he is
public final void removeInventoryEntry(ObjectWorld objectWorld) {
if (this.inventory.contains(objectWorld)) {
this.inventory.remove(objectWorld);
objectWorld.doUnInventorize();
objectWorld.setVisible();
objectWorld.setSonore();
}
}
//In a big community, lords will probably be notified about many vassals actions (requests from their vassals to access land...). This should rapidly create an
//unwanted network overload as well as a lot of unwanted junk data. A filter mechanism is proposed in which users can decide not to be notified when a
//vassal makes a request (the notification message is not send from the server). The filter is a list of pairs key-key value.
//The key mentions the Object concerned and the kind of request:
// Request field read - field write (of an object, tool, guide, avatar, land, World Spot)
// Request action execution (of an object, tool, guide, avatar, land, World Spot), the action may involve creation, modification or deletion of an object,
// tool, guide, avatar, land.
//The key-value is given with 6 booleans to grant notification about users' actions for the owner himself, lords, invited lords, peers, peers vassals and vassals.
//Using the notification filter, users can choose not to see or hear or collide from objects, tools, guides, avatars, owners (corresponding fields in the avatar).
//Users that make no filtering will have a log of every actions about themselves, lords, invited lords, peers, peers vassals and vassals. Since lords can also
//decide to refuse to be queried, this log can be incomplete. The only complete log is the one of the top lord if he requests no filtering.
//
//the notification filter mentions positive notification
//users are notified of nothing (except answers to their requests) if they do not request anything
//if a user want to be notified of a lord visiting one of its lands then he has to add en entry in the notification filter
//filter list is valid if its elements refer to different accessKind for every object
//originator for the filters must correspond to this user
public final Collection getFilters() {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Filter.class, true);
filter = new String("SELECT Filter FROM Filter f WHERE f.getRequester()==this");
query = this.getPersistenceManager().newQuery(Filter.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
return collection;
}
catch (Exception exception) {
return null;
}
}
public final void addFilter(Filter filter) {
Transaction transaction;
if ((filter!=null) && (filter.getRequester()==this)) {
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
this.getPersistenceManager().makePersistent(filter);
transaction.commit();
}
catch (Exception exception) {
}
}
}
private final void removeAllFilters() {
Transaction transaction;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
this.getPersistenceManager().deletePersistentAll(this.getFilters());
transaction.commit();
}
catch (Exception exception) {
}
}
public final void removeFilter(Filter filter) {
Transaction transaction;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
this.getPersistenceManager().deletePersistent(filter);
transaction.commit();
}
catch (Exception exception) {
}
}
public final Collection getBlockers() {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
// Execute a query
extent = this.getPersistenceManager().getExtent(Blocker.class, true);
filter = new String("SELECT Blocker FROM Blocker b WHERE b.getRequester()==this");
query = this.getPersistenceManager().newQuery(Blocker.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
return collection;
}
catch (Exception exception) {
return null;
}
}
//When a user doesn't want some automatic requests he sends to be completed
//for example if you want no to see anymore some kind of geometry that prevents easy movements in an are
//and provided you have the rights to do so, you can put a blocker in your avatar's blocker list to filter the
//automatic queries send to the server and therefore you won't hear or see from that object
//be aware that this is a way of reducing your capabilities in the virtual world
//if you prevent requests to be completed by putting a blocker you may be able to cut yourself access to the virtual world
//In that case, ask a super user to remove the faulty blockers by first building a new account or sending an e-mail.
//(list of faulty blockers is only checked about user ownership)
//a blocker is a kind of filter
//originator for the blockers must correspond to this user
//blocked object can't be owned by this avatar or be the avatar itself
public final void addBlocker(Blocker blocker) {
Transaction transaction;
if ((blocker!=null) && (blocker.getRequester()==this)) {
if (blocker.getObject() instanceof Avatar) {
if (((Avatar)blocker.getObject())!=this) {
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
this.getPersistenceManager().makePersistent(blocker);
transaction.commit();
}
catch (Exception exception) {
}
}}
else {
if (blocker.getObject() instanceof VirtualElement) {
if (((VirtualElement)blocker.getObject()).getUserOwner()!=this) {
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
this.getPersistenceManager().makePersistent(blocker);
transaction.commit();
}
catch (Exception exception) {
}
}}
}
}
}
public final void removeBlocker(Blocker blocker) {
Transaction transaction;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
this.getPersistenceManager().deletePersistent(blocker);
transaction.commit();
}
catch (Exception exception) {
}
}
private final void removeAllBlockers() {
Transaction transaction;
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
this.getPersistenceManager().deletePersistentAll(this.getBlockers());
transaction.commit();
}
catch (Exception exception) {
}
}
public final boolean getVassalsAccountCreation() {
return this.vassalsMayCreateAccounts;
}
//setting this parameter does not prevent a lord to refuse user creation
public final void setVassalsAccountCreation(boolean vassalsMayCreateAccounts) {
this.vassalsMayCreateAccounts = vassalsMayCreateAccounts;
}
//SYSTEM CALLS
public final static WorldSpot getWorldSpot(Avatar avatar) {
Skin skin;
Transform3D transform3D;
Point3d point3d;
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
if (avatar!=null) {
skin = avatar.getSkin();
transform3D = skin.getTransform3D();
point3d = new Point3d();
transform3D.transform(point3d);
try {
transaction = avatar.getPersistenceManager().currentTransaction();
transaction.begin();
extent = avatar.getPersistenceManager().getExtent(WorldSpot.class, true);
filter = new String("SELECT WorldSpot FROM WorldSpot w WHERE w.getBounds().intersect(point3d)");
query = avatar.getPersistenceManager().newQuery(WorldSpot.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
//each avatar should be in exactly one worldSpot at a time
return (WorldSpot) (collection.iterator().next());
}
catch (Exception exception) {
return null;
}
}
else {
return null;
}
}
//recursively get all vassals
//equivalent to a query with "SELECT Avatar FROM Avatar a WHERE Avatar.getFeudalRelation(a, this)=Avatar.VASSAL;
//but much faster
public final HashSet getAllVassals() {
return Avatar.getWorldSpot(this).getAllVassals(this);
}
//returns the land the avatar is currently on
//this land should be navigable for this user
public final Land getCurrentLand(Avatar avatar) {
Skin skin;
Transform3D transform3D;
Point3d point3d;
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
HashSet result;
if ((avatar!=null) && (avatar.getSkin()!=null)) {
skin = avatar.getSkin();
transform3D = skin.getTransform3D();
point3d = new Point3d();
transform3D.transform(point3d);
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Land.class, true);
filter = new String("SELECT Land FROM Land l WHERE l.getBounds().intersect(point3d)");
query = this.getPersistenceManager().newQuery(Land.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
result = this.filterCollection(avatar, collection);
if (result.size()>0) {
//each avatar should be in exactly one land at a time
return (Land) (result.iterator().next());
} else {
return null;
}
}
catch (Exception exception) {
return null;
}
}
else {
return null;
}
}
//returns null if the Avatar is already Avatar.Ruler
//could also be called getParent(Avatar avatar)
//please note this is a very computer intensive call
protected final static Avatar getLord(Avatar avatar) {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
if (avatar!=null) {
try {
transaction = avatar.getPersistenceManager().currentTransaction();
transaction.begin();
extent = avatar.getPersistenceManager().getExtent(Avatar.class, true);
filter = new String("SELECT Avatar FROM Avatar a WHERE a.getVassal().contains(avatar)");
query = avatar.getPersistenceManager().newQuery(Avatar.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
if (collection.size()==0) {
//avatar has no parent therefore it must be a ruler
return null;
}
else {
//every other avatar has exactely one parent
return (Avatar)collection.iterator().next();
}
}
catch (Exception exception) {
return null;
}
}
else {
return null;
}
}
//lords are returned from the top lord in decreasing order
//last of the list is the avatar himself (which is its own lord)
protected final static Vector getLords(Avatar avatar) {
//it is probably faster to get the ruler, build a tree from top to bottom until avatar is reached and
//get the direct list out of it
WorldSpot worldSpot;
Avatar currentAvatar;
HashSet currentPawns;
HashSet currentVassals;
Tree topTree;
Tree currentTree;
Tree[] treeElements;
Vector treeLevel;
boolean found;
int i;
Vector vector1;
Vector vector2;
Iterator iterator;
if (avatar!=null) {
worldSpot = avatar.getWorldSpot(avatar);
currentAvatar = worldSpot.getRuler();
topTree = new Tree(currentAvatar);
//build the tree of avatars
//avatar is belived to exist somewhere in the hierarchy
treeElements = new Tree[1];
treeElements[0] = topTree;
found = ((Avatar)topTree.getElement())==avatar;
while (!found) {
for (i=0; i<treeElements.length;i++) {
avatar = (Avatar) treeElements[i].getElement();
treeLevel = new Vector();
currentPawns = avatar.getPawns();
currentVassals = avatar.getVassals();
iterator = currentPawns.iterator();
for (i=0;i<currentPawns.size(); i++) {
currentTree = new Tree(iterator.next());
try {
treeElements[i].addChild(currentTree);
} catch (CyclicTreeException cyclicTreeException) {
}
treeLevel.add(currentTree);
found = (found || (((Avatar)currentTree.getElement())==avatar));
}
iterator = currentVassals.iterator();
for (i=0;i<currentVassals.size(); i++) {
currentTree = new Tree(iterator.next());
try {
treeElements[i].addChild(currentTree);
} catch (CyclicTreeException cyclicTreeException) {
}
treeLevel.add(currentTree);
found = (found || (((Avatar)currentTree.getElement())==avatar));
}
treeElements = (Tree[])treeLevel.toArray();
}
}
//find the avatar in the tree
i = 0;
found = false;
while (i<treeElements.length && (!found)) {
found = ((Avatar)treeElements[i].getElement())==avatar;
i++;
}
//build the resulting vector
vector1 = new Vector();
currentTree = treeElements[i-1];
while (currentTree!=null) {
vector1.add(currentTree.getElement());
currentTree = currentTree.getParent();
}
//return the Vector
vector2 = new Vector();
for (i=vector1.size()-1; i>=0;i--) {
vector2.add(vector1.elementAt(i));
}
return vector2;
}
else {
return null;
}
}
//given Avatar1 and Avatar2 (which SHOULD NOT BE null) this method returns the nearest common lord of both users
protected final static Avatar getCommonLord(Avatar avatar1, Avatar avatar2) {
Vector vector1;
Vector vector2;
int i;
boolean found;
if ((avatar1!=null) && (avatar2!=null)) {
vector1 = Avatar.getLords(avatar1);
vector2 = Avatar.getLords(avatar2);
if (vector1.get(0)!=vector2.get(0)) {
return null;
} else {
i=0;
found=false;
while ((i<Math.min(vector1.size(),vector2.size()))&&(found)) {
found = (vector1.get(i) == vector2.get(i));
i++;
}
return (Avatar) vector1.get(i-1);
}
}
else {
return null;
}
}
//avatars should be at least of length 1
protected final static Avatar getCommonLord(Avatar[] avatars) {
Vector[] vectors;
int i;
boolean found;
int minimum;
int j;
boolean good;
if (avatars!=null) {
vectors = new Vector[avatars.length];
for (i=0;i<avatars.length;i++) {
vectors[i] = Avatar.getLords(avatars[i]);
}
i=0;
found=false;
while ((i<avatars.length)&&(!found)) {
found = (vectors[0].get(0)!=vectors[i].get(0));
i++;
}
if (found) {
return null;
} else {
i=0;
minimum = vectors[0].size();
while (i<avatars.length) {
minimum = Math.min(minimum, vectors[i].size());
i++;
}
i=0;
found=false;
while ((i<minimum)&&(found)) {
j=0;
good=true;
while ((j<avatars.length)&&(good)) {
good = (vectors[0].get(i) == vectors[j].get(i));
j++;
}
found = (good && (j==avatars.length));
i++;
}
return (Avatar) vectors[0].get(i-1);
}
}
else {
return null;
}
}
//it is assumed that avatars are on the same worldSpot otherwise Avatar.NONE is returned
// avatar1 is the Avatar.XXXX of avatar2
public final static int getFeudalRelation(Avatar avatar1, Avatar avatar2) {
Vector vector1;
Vector vector2;
if ((avatar1!=null) && (avatar2!=null)) {
if (avatar1 == avatar2) {
return Avatar.SELF;
} else {
vector1 = Avatar.getLords(avatar1);
vector2 = Avatar.getLords(avatar2);
//vectors are at least of length 1
if (vector1.get(0)!=vector2.get(0)) {
return Avatar.NONE;
} else {
if (vector1.size()==vector2.size()) {
return Avatar.PEER;
} else {
if (vector1.size()>vector2.size()) {
if (vector2.lastElement()==vector1.get(vector2.size()-1)) {
return Avatar.VASSAL;
} else {
return Avatar.PEERSVASSAL;
}
} else {//vector1.size()<vector2.size()
if (vector1.lastElement()==vector2.get(vector1.size()-1)) {
return Avatar.LORD;
} else {
return Avatar.PEERSLORD;
}
}
}
}
}
}
else {
return Avatar.NONE;
}
}
//equivalent but not implemented as avatar.getlord()==null
public static final boolean isRuler(Avatar avatar) {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
if (avatar!=null) {
try {
transaction = avatar.getPersistenceManager().currentTransaction();
transaction.begin();
extent = avatar.getPersistenceManager().getExtent(Avatar.class, true);
filter = new String("SELECT WorldSpot FROM WorldSpot w WHERE w.getRuler()==avatar");
query = avatar.getPersistenceManager().newQuery(Avatar.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
return collection.contains(avatar);
}
catch (Exception exception) {
return false;
}
}
else {
return false;
}
}
//this call assumes valid user's rights (depending on who calls)
//otherwise the server returns a request refused message
//called by avatar at will and at creation time to be sure the login isn't reserved
//synchronisation should be done while effectively creating the new avatar
public final boolean checkLoginAndPasswordExists(LoginInformation loginInformation) {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
//checks against all users login stored in database
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Avatar.class, true);
filter = new String("SELECT Avatar FROM Avatar a WHERE a.getLoginInformation().checkLoginAndPassword(loginInformation)==true");
query = this.getPersistenceManager().newQuery(Avatar.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
return (collection.size()!=0);
}
catch (Exception exception) {
return false;
}
}
public final Avatar findAvatar(LoginInformation loginInformation) {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
//checks against all users login stored in database
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Avatar.class, true);
filter = new String("SELECT Avatar FROM Avatar a WHERE a.getLoginInformation().checkLoginAndPassword(loginInformation)==true");
query = this.getPersistenceManager().newQuery(Avatar.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
if (collection.size()!=0) {
//there should be at most one avatar
return (Avatar) collection.iterator().next();
} else {
return null;
}
}
catch (Exception exception) {
return null;
}
}
//valid only for non rulers (ie: parent may not be null)
//returns null if avatar can't be created
protected final static Avatar createUserAccount(LoginInformation loginInformation, Avatar parent) {
Transaction transaction;
Avatar avatar;
Vector avatars;
int i;
boolean found;
if (parent!=null) {
synchronized (parent) {
//all lords must agree account creation
avatars = Avatar.getLords(parent);
found=false;
i=0;
while ((i<avatars.size()) &&(found)) {
found = ((Avatar)avatars.get(i)).getVassalsAccountCreation();
i++;
}
if (!found) {
if (!parent.checkLoginAndPasswordExists(loginInformation)) {
avatar = new Avatar(parent.getUniverseServer(), loginInformation);
try {
transaction = parent.getPersistenceManager().currentTransaction();
transaction.begin();
parent.getPersistenceManager().makePersistent(avatar);
transaction.commit();
}
catch (Exception exception) {
}
//please also give a skin and a land to this avatar
//this also of navigation, information and socialization tools
//lands = parent.getLands();
//lands[0].split(XXX);
//avatar.addLand(XXX);
//avatar.setHomePosition(XXX);
//avatar.setSkin(XXX);
//skin.setTransform3D(this.getHomePosition());
//avatar.addInventoryEntry(XXX);
//this.createHome();
parent.createVassal(avatar);
} else {
avatar = null;
}
} else {
avatar = null;
}
}
} else {
avatar = null;
}
return avatar;
}
//does also work for self to reparent to someone else
//capabilities must be set accordingly
//if newParent is a vassal or a pawn of avatar then its vassals are reparented to avatar's lord
//if not, every vassal and pawn is moved along with avatar and therefore keep its parental relationship with avatar
//rulers can be reparented if they have one only child
//you can't reparent to yourself
//moveUserInHierarchy(this,this.getLord()) is a null operation
//to reparent an avatar as a ruler moveUserInHierarchy(avatar, null);
protected final static void moveUserInHierarchy(Avatar avatar, Avatar newParent) {
Avatar oldRuler;
Avatar vassal;
if (avatar!=null) {
if ((newParent!=null) && (avatar!=newParent)) {
oldRuler = Avatar.getWorldSpot(avatar).getRuler();
if ((newParent==null) && (oldRuler.getVassals().size()==1) && (avatar!=oldRuler)) {
//avatar becomes new ruler
synchronized (avatar) {
Avatar.getLord(avatar).killVassal(avatar);
avatar.createVassal(oldRuler);
avatar.updateAccount();
}
} else {
if ((oldRuler==avatar) && (avatar.getVassals().size()==1) && (newParent!=null)) {
//Ruler avatar is moved down in the hierarchy
synchronized (avatar) {
vassal = (Avatar)avatar.getVassals().iterator().next();
Avatar.getWorldSpot(avatar).setRuler(vassal);
avatar.killVassal(vassal);
newParent.createVassal(avatar);
avatar.updateAccount();
}
} else {
if (newParent!=null) {
//avatar is normally reparented
synchronized (avatar) {
Avatar.getLord(avatar).killVassal(avatar);
newParent.createVassal(avatar);
avatar.updateAccount();
}
}
}
}
}
}
}
//agreeingParent must be a lord of avatar and of new parent
public final static void moveUserInHierarchy(Avatar avatar, Avatar newParent, Avatar agreeingParent) {
if ((avatar != null) && (newParent != null) && (agreeingParent != null)) {
if ((Avatar.getLords(avatar).contains(agreeingParent)) && (Avatar.getLords(newParent).contains(agreeingParent))) {
Avatar.moveUserInHierarchy(avatar, newParent);
}
}
}
//updates position, inventory, skin, possessions (backgoundWorlds, objectWorlds),
//lands access for others objectWorlds, backgroundWorlds and avatars
protected final void updateAccount() {
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
ObjectWorld objectWorld;
BackgroundWorld backgroundWorld;
Land land;
Iterator iterator;
if (this.getHomePosition()==null) {
this.createHome();
}
//checks against all users login stored in database
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Avatar.class, true);
filter = new String("SELECT * FROM ObjectWorld o WHERE o.getUserOwner()==this");
query = this.getPersistenceManager().newQuery(Avatar.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
iterator = collection.iterator();
while (iterator.hasNext()) {
objectWorld = (ObjectWorld) iterator.next();
objectWorld.refreshTransform();
}
}
catch (Exception exception) {
}
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Avatar.class, true);
filter = new String("SELECT * FROM BackgroundWorld b WHERE b.getUserOwner()==this");
query = this.getPersistenceManager().newQuery(Avatar.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
iterator = collection.iterator();
while (iterator.hasNext()) {
backgroundWorld = (BackgroundWorld) iterator.next();
backgroundWorld.refreshBounds();
}
}
catch (Exception exception) {
}
try {
transaction = this.getPersistenceManager().currentTransaction();
transaction.begin();
extent = this.getPersistenceManager().getExtent(Avatar.class, true);
filter = new String("SELECT * FROM Land l WHERE l.getUserOwner()==this");
query = this.getPersistenceManager().newQuery(Avatar.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
iterator = collection.iterator();
while (iterator.hasNext()) {
land = (Land) iterator.next();
land.refreshContents();
}
}
catch (Exception exception) {
}
}
//delete user account (actually release database resources)
//pass, tickets and receipts should be deleted at this step
//pool of avatar's belonging should be emptied of avatar's belongings for this command to succeed
//cannot delete ruler
//you have to pawn user before deleting account
//boolean returns success or failure (avatar still exists)
protected final static boolean deleteUserAccount(Avatar avatar) {
Avatar lord;
Transaction transaction;
Extent extent;
Query query;
String filter;
Collection collection;
Iterator iterator;
Pass pass1;
Pass pass2;
Vector vector;
boolean success;
success = false;
if (avatar!=null) {
lord = Avatar.getLord(avatar);
if (lord!=null) {
//verify this is a pawn
if (avatar.getStatus()==Avatar.PAWN) {
//verify every belonging has been removed or transfered
//elements not drawn from pool are still with this owner
synchronized (avatar) {
try {
transaction = avatar.getPersistenceManager().currentTransaction();
transaction.begin();
extent = avatar.getPersistenceManager().getExtent(VirtualElement.class, true);
filter = new String("SELECT VirtualElement FROM VirtualElement v WHERE v.getUserOwner()==avatar)");
query = avatar.getPersistenceManager().newQuery(VirtualElement.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
if (collection.size()==0) {
//pass, tickets and receipts should be deleted at this step
avatar.removeAllPass();
//reparent pass emited by avatar and still in use by others avatars
try {
transaction = avatar.getPersistenceManager().currentTransaction();
transaction.begin();
extent = avatar.getPersistenceManager().getExtent(Pass.class, true);
filter = new String("SELECT Pass FROM Pass p WHERE p.getSender()==avatar");
query = avatar.getPersistenceManager().newQuery(Pass.class, extent, filter);
collection = (Collection) query.execute();
transaction.commit();
iterator = collection.iterator();
vector = new Vector();
while (iterator.hasNext()) {
pass1 = (Pass)iterator.next();
pass2 = new Pass(pass1.getName(), pass1.getStartDate(), pass1.getEndDate(), lord, pass1.getReceiver(), pass1.getObject());
vector.add(pass2);
}
try {
transaction = avatar.getPersistenceManager().currentTransaction();
transaction.begin();
avatar.getPersistenceManager().deletePersistentAll(collection.toArray());
avatar.getPersistenceManager().makePersistentAll(vector.toArray());
transaction.commit();
}
catch (Exception exception) {
}
avatar.removeAllTicketOrReceipts();
//collapse hierarchy (notify parents and vassals)
iterator = avatar.getVassals().iterator();
while (iterator.hasNext()) {
Avatar.moveUserInHierarchy((Avatar)iterator.next(), lord);
}
lord.killVassal(avatar);
try {
transaction = avatar.getPersistenceManager().currentTransaction();
transaction.begin();
avatar.getPersistenceManager().deletePersistent(avatar);
transaction.commit();
}
catch (Exception exception) {
}
success = true;
}
catch (Exception exception) {
}
}
}
catch (Exception exception) {
}
}
}
}
}
return success;
}
//agreeingParent should be a lord of avatar
public final static boolean deleteUserAccount(Avatar avatar, Avatar agreeingParent) {
int relation;
if ((avatar != null) && (agreeingParent != null)) {
relation = Avatar.getFeudalRelation(avatar, agreeingParent);
if ((relation == Avatar.LORD) || (relation == Avatar.SELF)) {
return Avatar.deleteUserAccount(avatar);
} else {
return false;
}
} else {
return false;
}
}
private final static HashSet filterCollection(Avatar caller, Collection collection) {
Iterator iterator;
Iterator iterator2;
Collection passCollection;
HashSet resultCollection;
int relation;
VirtualElement virtualElement;
boolean found;
Pass pass;
resultCollection = new HashSet();
if (caller!=null) {
//objects with a pass may bypass access
passCollection = caller.getPass();
iterator = collection.iterator();
while (iterator.hasNext()) {
virtualElement = (VirtualElement) iterator.next();
relation = Avatar.getFeudalRelation(caller, virtualElement.getUserOwner());
if ((relation == Avatar.LORD) || (relation == Avatar.SELF)) {
resultCollection.add(virtualElement);
} else {
if (virtualElement.getOthersAccessRights(relation) != VirtualElement.HIDDEN) {
resultCollection.add(virtualElement);
} else {
iterator2 = passCollection.iterator();
found = false;
while (iterator2.hasNext() && (!found)) {
pass = (Pass) iterator2.next();
found = (pass.getObject() == virtualElement);
}
if (found) {
resultCollection.add(virtualElement);
}
}
}
}
}
return resultCollection;
}
}
|