2014-10-02 14:02:07 +02:00
package com.massivecraft.factions.entity ;
2017-03-30 00:03:58 +02:00
import com.massivecraft.factions.AccessStatus ;
2014-10-02 14:02:07 +02:00
import com.massivecraft.factions.Perm ;
import com.massivecraft.factions.Rel ;
import com.massivecraft.factions.TerritoryAccess ;
2016-02-25 22:28:09 +01:00
import com.massivecraft.factions.cmd.CmdFactions ;
2014-11-19 11:24:36 +01:00
import com.massivecraft.factions.event.EventFactionsCreatePerms ;
2015-11-06 02:10:29 +01:00
import com.massivecraft.massivecore.Named ;
2014-10-02 14:02:07 +02:00
import com.massivecraft.massivecore.Prioritized ;
import com.massivecraft.massivecore.Registerable ;
2018-01-19 20:51:07 +01:00
import com.massivecraft.massivecore.collections.MassiveSet ;
2016-04-24 08:38:51 +02:00
import com.massivecraft.massivecore.comparator.ComparatorSmart ;
2016-02-03 05:06:49 +01:00
import com.massivecraft.massivecore.predicate.PredicateIsRegistered ;
2014-10-02 14:02:07 +02:00
import com.massivecraft.massivecore.ps.PS ;
import com.massivecraft.massivecore.store.Entity ;
import com.massivecraft.massivecore.util.MUtil ;
import com.massivecraft.massivecore.util.Txt ;
2017-03-24 13:05:58 +01:00
import org.bukkit.entity.Player ;
import java.util.ArrayList ;
import java.util.List ;
import java.util.Set ;
2014-10-02 14:02:07 +02:00
2015-11-06 02:10:29 +01:00
public class MPerm extends Entity < MPerm > implements Prioritized , Registerable , Named
2014-10-02 14:02:07 +02:00
{
// -------------------------------------------- //
// CONSTANTS
// -------------------------------------------- //
public final static transient String ID_BUILD = " build " ;
public final static transient String ID_PAINBUILD = " painbuild " ;
public final static transient String ID_DOOR = " door " ;
public final static transient String ID_BUTTON = " button " ;
public final static transient String ID_LEVER = " lever " ;
public final static transient String ID_CONTAINER = " container " ;
2014-10-03 09:01:36 +02:00
public final static transient String ID_NAME = " name " ;
public final static transient String ID_DESC = " desc " ;
public final static transient String ID_MOTD = " motd " ;
2014-10-02 14:02:07 +02:00
public final static transient String ID_INVITE = " invite " ;
public final static transient String ID_KICK = " kick " ;
2014-10-03 09:01:36 +02:00
public final static transient String ID_TITLE = " title " ;
public final static transient String ID_HOME = " home " ;
2014-10-02 14:02:07 +02:00
public final static transient String ID_SETHOME = " sethome " ;
2014-10-07 12:30:44 +02:00
public final static transient String ID_DEPOSIT = " deposit " ;
2014-10-02 14:02:07 +02:00
public final static transient String ID_WITHDRAW = " withdraw " ;
public final static transient String ID_TERRITORY = " territory " ;
public final static transient String ID_ACCESS = " access " ;
2014-10-07 17:08:40 +02:00
public final static transient String ID_CLAIMNEAR = " claimnear " ;
2014-10-03 09:01:36 +02:00
public final static transient String ID_REL = " rel " ;
2014-10-02 14:02:07 +02:00
public final static transient String ID_DISBAND = " disband " ;
public final static transient String ID_FLAGS = " flags " ;
public final static transient String ID_PERMS = " perms " ;
public final static transient int PRIORITY_BUILD = 1000 ;
public final static transient int PRIORITY_PAINBUILD = 2000 ;
public final static transient int PRIORITY_DOOR = 3000 ;
public final static transient int PRIORITY_BUTTON = 4000 ;
public final static transient int PRIORITY_LEVER = 5000 ;
public final static transient int PRIORITY_CONTAINER = 6000 ;
2014-10-03 09:01:36 +02:00
public final static transient int PRIORITY_NAME = 7000 ;
public final static transient int PRIORITY_DESC = 8000 ;
public final static transient int PRIORITY_MOTD = 9000 ;
public final static transient int PRIORITY_INVITE = 10000 ;
public final static transient int PRIORITY_KICK = 11000 ;
public final static transient int PRIORITY_TITLE = 12000 ;
public final static transient int PRIORITY_HOME = 13000 ;
public final static transient int PRIORITY_SETHOME = 14000 ;
2014-10-07 12:30:44 +02:00
public final static transient int PRIORITY_DEPOSIT = 15000 ;
public final static transient int PRIORITY_WITHDRAW = 16000 ;
public final static transient int PRIORITY_TERRITORY = 17000 ;
public final static transient int PRIORITY_ACCESS = 18000 ;
2014-10-07 17:08:40 +02:00
public final static transient int PRIORITY_CLAIMNEAR = 19000 ;
public final static transient int PRIORITY_REL = 20000 ;
public final static transient int PRIORITY_DISBAND = 21000 ;
public final static transient int PRIORITY_FLAGS = 22000 ;
public final static transient int PRIORITY_PERMS = 23000 ;
2014-10-02 14:02:07 +02:00
// -------------------------------------------- //
// META: CORE
// -------------------------------------------- //
public static MPerm get ( Object oid )
{
return MPermColl . get ( ) . get ( oid ) ;
}
public static List < MPerm > getAll ( )
{
2014-11-19 11:24:36 +01:00
return getAll ( false ) ;
}
public static List < MPerm > getAll ( boolean isAsync )
{
setupStandardPerms ( ) ;
new EventFactionsCreatePerms ( ) . run ( ) ;
2016-04-24 08:38:51 +02:00
return MPermColl . get ( ) . getAll ( PredicateIsRegistered . get ( ) , ComparatorSmart . get ( ) ) ;
2014-10-02 14:02:07 +02:00
}
public static void setupStandardPerms ( )
{
2014-10-03 09:01:36 +02:00
getPermBuild ( ) ;
getPermPainbuild ( ) ;
getPermDoor ( ) ;
getPermButton ( ) ;
getPermLever ( ) ;
getPermContainer ( ) ;
2014-10-02 14:02:07 +02:00
2014-10-03 09:01:36 +02:00
getPermName ( ) ;
getPermDesc ( ) ;
getPermMotd ( ) ;
getPermInvite ( ) ;
getPermKick ( ) ;
getPermTitle ( ) ;
getPermHome ( ) ;
getPermSethome ( ) ;
2014-10-07 12:30:44 +02:00
getPermDeposit ( ) ;
2014-10-03 09:01:36 +02:00
getPermWithdraw ( ) ;
getPermTerritory ( ) ;
getPermAccess ( ) ;
2014-10-07 17:08:40 +02:00
getPermClaimnear ( ) ;
2014-10-03 09:01:36 +02:00
getPermRel ( ) ;
getPermDisband ( ) ;
getPermFlags ( ) ;
getPermPerms ( ) ;
2014-10-02 14:02:07 +02:00
}
2014-10-16 18:03:58 +02:00
public static MPerm getPermBuild ( ) { return getCreative ( PRIORITY_BUILD , ID_BUILD , ID_BUILD , " edit the terrain " , MUtil . set ( Rel . LEADER , Rel . OFFICER , Rel . MEMBER ) , true , true , true ) ; }
2018-01-19 20:51:07 +01:00
public static MPerm getPermPainbuild ( ) { return getCreative ( PRIORITY_PAINBUILD , ID_PAINBUILD , ID_PAINBUILD , " edit, take damage " , new MassiveSet < Rel > ( ) , true , true , true ) ; }
2014-10-03 09:01:36 +02:00
public static MPerm getPermDoor ( ) { return getCreative ( PRIORITY_DOOR , ID_DOOR , ID_DOOR , " use doors " , MUtil . set ( Rel . LEADER , Rel . OFFICER , Rel . MEMBER , Rel . RECRUIT , Rel . ALLY ) , true , true , true ) ; }
public static MPerm getPermButton ( ) { return getCreative ( PRIORITY_BUTTON , ID_BUTTON , ID_BUTTON , " use stone buttons " , MUtil . set ( Rel . LEADER , Rel . OFFICER , Rel . MEMBER , Rel . RECRUIT , Rel . ALLY ) , true , true , true ) ; }
public static MPerm getPermLever ( ) { return getCreative ( PRIORITY_LEVER , ID_LEVER , ID_LEVER , " use levers " , MUtil . set ( Rel . LEADER , Rel . OFFICER , Rel . MEMBER , Rel . RECRUIT , Rel . ALLY ) , true , true , true ) ; }
public static MPerm getPermContainer ( ) { return getCreative ( PRIORITY_CONTAINER , ID_CONTAINER , ID_CONTAINER , " use containers " , MUtil . set ( Rel . LEADER , Rel . OFFICER , Rel . MEMBER ) , true , true , true ) ; }
2014-10-02 14:02:07 +02:00
2014-10-03 09:01:36 +02:00
public static MPerm getPermName ( ) { return getCreative ( PRIORITY_NAME , ID_NAME , ID_NAME , " set name " , MUtil . set ( Rel . LEADER ) , false , true , true ) ; }
public static MPerm getPermDesc ( ) { return getCreative ( PRIORITY_DESC , ID_DESC , ID_DESC , " set description " , MUtil . set ( Rel . LEADER , Rel . OFFICER ) , false , true , true ) ; }
public static MPerm getPermMotd ( ) { return getCreative ( PRIORITY_MOTD , ID_MOTD , ID_MOTD , " set motd " , MUtil . set ( Rel . LEADER , Rel . OFFICER ) , false , true , true ) ; }
public static MPerm getPermInvite ( ) { return getCreative ( PRIORITY_INVITE , ID_INVITE , ID_INVITE , " invite players " , MUtil . set ( Rel . LEADER , Rel . OFFICER ) , false , true , true ) ; }
public static MPerm getPermKick ( ) { return getCreative ( PRIORITY_KICK , ID_KICK , ID_KICK , " kick members " , MUtil . set ( Rel . LEADER , Rel . OFFICER ) , false , true , true ) ; }
public static MPerm getPermTitle ( ) { return getCreative ( PRIORITY_TITLE , ID_TITLE , ID_TITLE , " set titles " , MUtil . set ( Rel . LEADER , Rel . OFFICER ) , false , true , true ) ; }
public static MPerm getPermHome ( ) { return getCreative ( PRIORITY_HOME , ID_HOME , ID_HOME , " teleport home " , MUtil . set ( Rel . LEADER , Rel . OFFICER , Rel . MEMBER , Rel . RECRUIT , Rel . ALLY ) , false , true , true ) ; }
public static MPerm getPermSethome ( ) { return getCreative ( PRIORITY_SETHOME , ID_SETHOME , ID_SETHOME , " set the home " , MUtil . set ( Rel . LEADER , Rel . OFFICER ) , false , true , true ) ; }
2014-10-16 18:03:58 +02:00
public static MPerm getPermDeposit ( ) { return getCreative ( PRIORITY_DEPOSIT , ID_DEPOSIT , ID_DEPOSIT , " deposit money " , MUtil . set ( Rel . LEADER , Rel . OFFICER , Rel . MEMBER , Rel . RECRUIT , Rel . ALLY , Rel . TRUCE , Rel . NEUTRAL , Rel . ENEMY ) , false , false , false ) ; } // non editable, non visible.
public static MPerm getPermWithdraw ( ) { return getCreative ( PRIORITY_WITHDRAW , ID_WITHDRAW , ID_WITHDRAW , " withdraw money " , MUtil . set ( Rel . LEADER ) , false , true , true ) ; }
2014-10-03 09:01:36 +02:00
public static MPerm getPermTerritory ( ) { return getCreative ( PRIORITY_TERRITORY , ID_TERRITORY , ID_TERRITORY , " claim or unclaim " , MUtil . set ( Rel . LEADER , Rel . OFFICER ) , false , true , true ) ; }
public static MPerm getPermAccess ( ) { return getCreative ( PRIORITY_ACCESS , ID_ACCESS , ID_ACCESS , " grant territory " , MUtil . set ( Rel . LEADER , Rel . OFFICER ) , false , true , true ) ; }
2014-10-07 17:08:40 +02:00
public static MPerm getPermClaimnear ( ) { return getCreative ( PRIORITY_CLAIMNEAR , ID_CLAIMNEAR , ID_CLAIMNEAR , " claim nearby " , MUtil . set ( Rel . LEADER , Rel . OFFICER , Rel . MEMBER , Rel . RECRUIT , Rel . ALLY ) , false , false , false ) ; } // non editable, non visible.
2014-10-03 09:01:36 +02:00
public static MPerm getPermRel ( ) { return getCreative ( PRIORITY_REL , ID_REL , ID_REL , " change relations " , MUtil . set ( Rel . LEADER , Rel . OFFICER ) , false , true , true ) ; }
public static MPerm getPermDisband ( ) { return getCreative ( PRIORITY_DISBAND , ID_DISBAND , ID_DISBAND , " disband the faction " , MUtil . set ( Rel . LEADER ) , false , true , true ) ; }
public static MPerm getPermFlags ( ) { return getCreative ( PRIORITY_FLAGS , ID_FLAGS , ID_FLAGS , " manage flags " , MUtil . set ( Rel . LEADER ) , false , true , true ) ; }
public static MPerm getPermPerms ( ) { return getCreative ( PRIORITY_PERMS , ID_PERMS , ID_PERMS , " manage permissions " , MUtil . set ( Rel . LEADER ) , false , true , true ) ; }
2014-10-02 14:02:07 +02:00
public static MPerm getCreative ( int priority , String id , String name , String desc , Set < Rel > standard , boolean territory , boolean editable , boolean visible )
{
MPerm ret = MPermColl . get ( ) . get ( id , false ) ;
if ( ret ! = null )
{
ret . setRegistered ( true ) ;
return ret ;
}
ret = new MPerm ( priority , name , desc , standard , territory , editable , visible ) ;
MPermColl . get ( ) . attach ( ret , id ) ;
ret . setRegistered ( true ) ;
ret . sync ( ) ;
return ret ;
}
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
@Override
public MPerm load ( MPerm that )
{
this . priority = that . priority ;
this . name = that . name ;
this . desc = that . desc ;
this . standard = that . standard ;
this . territory = that . territory ;
this . editable = that . editable ;
this . visible = that . visible ;
return this ;
}
// -------------------------------------------- //
// TRANSIENT FIELDS (Registered)
// -------------------------------------------- //
private transient boolean registered = false ;
public boolean isRegistered ( ) { return this . registered ; }
public void setRegistered ( boolean registered ) { this . registered = registered ; }
// -------------------------------------------- //
// FIELDS
// -------------------------------------------- //
2014-11-18 09:49:11 +01:00
// The sort priority. Low values appear first in sorted lists.
// 1 is high up, 99999 is far down.
// Standard Faction perms use "thousand values" like 1000, 2000, 3000 etc to allow adding new perms inbetween.
// So 1000 might sound like a lot but it's actually the priority for the first perm.
2014-10-02 14:02:07 +02:00
private int priority = 0 ;
@Override public int getPriority ( ) { return this . priority ; }
public MPerm setPriority ( int priority ) { this . priority = priority ; this . changed ( ) ; return this ; }
2014-11-18 09:49:11 +01:00
// The name of the perm. According to standard it should be fully lowercase just like the perm id.
// In fact the name and the id of all standard perms are the same.
// I just added the name in case anyone feel like renaming their perms for some reason.
2014-10-02 14:02:07 +02:00
// Example: "build"
private String name = " defaultName " ;
2015-11-06 02:10:29 +01:00
@Override public String getName ( ) { return this . name ; }
2014-10-02 14:02:07 +02:00
public MPerm setName ( String name ) { this . name = name ; this . changed ( ) ; return this ; }
2014-11-18 09:49:11 +01:00
// The perm function described as an "order".
// The desc should match the format:
// "You are not allowed to X."
// "You are not allowed to edit the terrain."
2014-10-02 14:02:07 +02:00
// Example: "edit the terrain"
private String desc = " defaultDesc " ;
public String getDesc ( ) { return this . desc ; }
public MPerm setDesc ( String desc ) { this . desc = desc ; this . changed ( ) ; return this ; }
2014-11-18 09:49:11 +01:00
// What is the standard (aka default) perm value?
// This value will be set for factions from the beginning.
2014-10-02 14:02:07 +02:00
// Example: ... set of relations ...
2018-01-19 20:51:07 +01:00
private Set < Rel > standard = new MassiveSet < > ( ) ;
2014-10-02 14:02:07 +02:00
public Set < Rel > getStandard ( ) { return this . standard ; }
public MPerm setStandard ( Set < Rel > standard ) { this . standard = standard ; this . changed ( ) ; return this ; }
2014-11-18 09:49:11 +01:00
// Is this a territory perm meaning it has to do with territory construction, modification or interaction?
// True Examples: build, container, door, lever etc.
// False Examples: name, invite, home, sethome, deposit, withdraw etc.
2014-10-02 14:02:07 +02:00
private boolean territory = false ;
public boolean isTerritory ( ) { return this . territory ; }
public MPerm setTerritory ( boolean territory ) { this . territory = territory ; this . changed ( ) ; return this ; }
2014-11-18 09:49:11 +01:00
// Is this perm editable by players?
// With this we mean standard non administrator players.
2016-02-02 19:03:11 +01:00
// All perms can be changed using /f override.
2014-10-02 14:02:07 +02:00
// Example: true (all perms are editable by default)
private boolean editable = false ;
public boolean isEditable ( ) { return this . editable ; }
public MPerm setEditable ( boolean editable ) { this . editable = editable ; this . changed ( ) ; return this ; }
2014-11-18 09:49:11 +01:00
// Is this perm visible to players?
// With this we mean standard non administrator players.
2016-02-02 19:03:11 +01:00
// All perms can be seen using /f override.
2014-11-18 09:49:11 +01:00
// Some perms can be rendered meaningless by settings in Factions or external plugins.
// Say we set "editable" to false.
// In such case we might want to hide the perm by setting "visible" false.
// If it can't be changed, why bother showing it?
2014-10-02 14:02:07 +02:00
// Example: true (yeah we need to see this permission)
private boolean visible = true ;
public boolean isVisible ( ) { return this . visible ; }
public MPerm setVisible ( boolean visible ) { this . visible = visible ; this . changed ( ) ; return this ; }
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public MPerm ( )
{
// No argument constructor for GSON
}
public MPerm ( int priority , String name , String desc , Set < Rel > standard , boolean territory , boolean editable , boolean visible )
{
this . priority = priority ;
this . name = name ;
this . desc = desc ;
this . standard = standard ;
this . territory = territory ;
this . editable = editable ;
this . visible = visible ;
}
// -------------------------------------------- //
// EXTRAS
// -------------------------------------------- //
public String createDeniedMessage ( MPlayer mplayer , Faction hostFaction )
{
2014-10-03 11:48:25 +02:00
// Null Check
if ( mplayer = = null ) throw new NullPointerException ( " mplayer " ) ;
if ( hostFaction = = null ) throw new NullPointerException ( " hostFaction " ) ;
2014-10-02 14:02:07 +02:00
String ret = Txt . parse ( " %s<b> does not allow you to %s<b>. " , hostFaction . describeTo ( mplayer , true ) , this . getDesc ( ) ) ;
2014-10-03 11:48:25 +02:00
Player player = mplayer . getPlayer ( ) ;
2016-02-02 19:03:11 +01:00
if ( player ! = null & & Perm . OVERRIDE . has ( player ) )
2014-10-02 14:02:07 +02:00
{
2016-02-25 22:28:09 +01:00
ret + = Txt . parse ( " \ n<i>You can bypass by using " + CmdFactions . get ( ) . cmdFactionsOverride . getTemplate ( false ) . toPlain ( true ) ) ;
2014-10-02 14:02:07 +02:00
}
2014-10-03 11:48:25 +02:00
2014-10-02 14:02:07 +02:00
return ret ;
}
2015-01-09 22:36:12 +01:00
public String getDesc ( boolean withName , boolean withDesc )
{
2017-03-24 14:03:29 +01:00
List < String > parts = new ArrayList < > ( ) ;
2015-01-09 22:36:12 +01:00
if ( withName )
{
String nameFormat ;
if ( ! this . isVisible ( ) )
{
nameFormat = " <silver>%s " ;
}
else if ( this . isEditable ( ) )
{
nameFormat = " <pink>%s " ;
}
else
{
nameFormat = " <aqua>%s " ;
}
String name = this . getName ( ) ;
String nameDesc = Txt . parse ( nameFormat , name ) ;
parts . add ( nameDesc ) ;
}
if ( withDesc )
{
String desc = this . getDesc ( ) ;
String descDesc = Txt . parse ( " <i>%s " , desc ) ;
parts . add ( descDesc ) ;
}
return Txt . implode ( parts , " " ) ;
}
2014-10-02 14:02:07 +02:00
public boolean has ( Faction faction , Faction hostFaction )
{
2014-10-03 11:48:25 +02:00
// Null Check
if ( faction = = null ) throw new NullPointerException ( " faction " ) ;
if ( hostFaction = = null ) throw new NullPointerException ( " hostFaction " ) ;
2014-10-02 14:02:07 +02:00
Rel rel = faction . getRelationTo ( hostFaction ) ;
2014-11-13 11:41:21 +01:00
return hostFaction . isPermitted ( this , rel ) ;
2014-10-02 14:02:07 +02:00
}
public boolean has ( MPlayer mplayer , Faction hostFaction , boolean verboose )
{
2014-10-03 11:48:25 +02:00
// Null Check
if ( mplayer = = null ) throw new NullPointerException ( " mplayer " ) ;
if ( hostFaction = = null ) throw new NullPointerException ( " hostFaction " ) ;
2016-02-02 19:03:11 +01:00
if ( mplayer . isOverriding ( ) ) return true ;
2014-10-02 14:02:07 +02:00
Rel rel = mplayer . getRelationTo ( hostFaction ) ;
2014-11-13 11:41:21 +01:00
if ( hostFaction . isPermitted ( this , rel ) ) return true ;
2014-10-02 14:02:07 +02:00
2015-09-03 09:33:01 +02:00
if ( verboose ) mplayer . message ( this . createDeniedMessage ( mplayer , hostFaction ) ) ;
2014-10-02 14:02:07 +02:00
return false ;
}
public boolean has ( MPlayer mplayer , PS ps , boolean verboose )
{
2014-10-03 11:48:25 +02:00
// Null Check
if ( mplayer = = null ) throw new NullPointerException ( " mplayer " ) ;
if ( ps = = null ) throw new NullPointerException ( " ps " ) ;
2016-02-02 19:03:11 +01:00
if ( mplayer . isOverriding ( ) ) return true ;
2014-10-02 14:02:07 +02:00
TerritoryAccess ta = BoardColl . get ( ) . getTerritoryAccessAt ( ps ) ;
Faction hostFaction = ta . getHostFaction ( ) ;
if ( this . isTerritory ( ) )
{
2017-03-30 00:03:58 +02:00
AccessStatus accessStatus = ta . getTerritoryAccess ( mplayer ) ;
if ( accessStatus ! = AccessStatus . STANDARD )
2014-10-02 14:02:07 +02:00
{
2017-03-30 00:03:58 +02:00
if ( verboose & & accessStatus = = AccessStatus . DECREASED )
2014-10-02 14:02:07 +02:00
{
2015-09-03 09:33:01 +02:00
mplayer . message ( this . createDeniedMessage ( mplayer , hostFaction ) ) ;
2014-10-02 14:02:07 +02:00
}
2017-03-30 00:03:58 +02:00
return accessStatus . hasAccess ( ) ;
2014-10-02 14:02:07 +02:00
}
}
return this . has ( mplayer , hostFaction , verboose ) ;
}
// -------------------------------------------- //
// UTIL: ASCII
// -------------------------------------------- //
public static String getStateHeaders ( )
{
String ret = " " ;
for ( Rel rel : Rel . values ( ) )
{
ret + = rel . getColor ( ) . toString ( ) ;
ret + = rel . toString ( ) . substring ( 0 , 3 ) ;
ret + = " " ;
}
return ret ;
}
public String getStateInfo ( Set < Rel > value , boolean withDesc )
{
String ret = " " ;
for ( Rel rel : Rel . values ( ) )
{
if ( value . contains ( rel ) )
{
ret + = " <g>YES " ;
}
else
{
ret + = " <b>NOO " ;
}
ret + = " " ;
}
String color = " <aqua> " ;
if ( ! this . isVisible ( ) )
{
color = " <silver> " ;
}
else if ( this . isEditable ( ) )
{
color = " <pink> " ;
}
ret + = color ;
ret + = this . getName ( ) ;
ret = Txt . parse ( ret ) ;
if ( withDesc ) ret + = " <i> " + this . getDesc ( ) ;
return ret ;
}
}