<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://osimdev.org/wiki/index.php?action=history&amp;feed=atom&amp;title=OpenSimulator_Internals%2FCode_Map%2FROBUST%2FLoginService</id>
	<title>OpenSimulator Internals/Code Map/ROBUST/LoginService - Revision history</title>
	<link rel="self" type="application/atom+xml" href="http://osimdev.org/wiki/index.php?action=history&amp;feed=atom&amp;title=OpenSimulator_Internals%2FCode_Map%2FROBUST%2FLoginService"/>
	<link rel="alternate" type="text/html" href="http://osimdev.org/wiki/index.php?title=OpenSimulator_Internals/Code_Map/ROBUST/LoginService&amp;action=history"/>
	<updated>2026-08-04T16:56:26Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>http://osimdev.org/wiki/index.php?title=OpenSimulator_Internals/Code_Map/ROBUST/LoginService&amp;diff=65&amp;oldid=prev</id>
		<title>Jwbshaw: first</title>
		<link rel="alternate" type="text/html" href="http://osimdev.org/wiki/index.php?title=OpenSimulator_Internals/Code_Map/ROBUST/LoginService&amp;diff=65&amp;oldid=prev"/>
		<updated>2026-07-07T12:05:07Z</updated>

		<summary type="html">&lt;p&gt;first&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;= OpenSimulator Internals/Code Map/ROBUST/LoginService =&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
LLLoginService handles viewer login requests. It is the most complex ROBUST service -- it orchestrates authentication, inventory, presence, destination selection, and agent launch across multiple other services.&lt;br /&gt;
&lt;br /&gt;
Source file:&lt;br /&gt;
&lt;br /&gt;
 OpenSim/Services/LLLoginService/LLLoginService.cs&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Constructor and Config Loading ===&lt;br /&gt;
&lt;br /&gt;
 LLLoginService(IConfigSource config, ISimulationService simService, ILibraryService libraryService)&lt;br /&gt;
&lt;br /&gt;
[LoginService] section is required -- throws if absent.&lt;br /&gt;
&lt;br /&gt;
Services loaded from [LoginService]:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Key !! Interface !! Required&lt;br /&gt;
|-&lt;br /&gt;
| UserAccountService || IUserAccountService || Yes -- throws if empty&lt;br /&gt;
|-&lt;br /&gt;
| AuthenticationService || IAuthenticationService || Yes -- throws if empty&lt;br /&gt;
|-&lt;br /&gt;
| GridUserService || IGridUserService || No&lt;br /&gt;
|-&lt;br /&gt;
| UserAgentService || IUserAgentService || No&lt;br /&gt;
|-&lt;br /&gt;
| InventoryService || IInventoryService || No&lt;br /&gt;
|-&lt;br /&gt;
| GridService || IGridService || No&lt;br /&gt;
|-&lt;br /&gt;
| PresenceService || IPresenceService || No&lt;br /&gt;
|-&lt;br /&gt;
| AvatarService || IAvatarService || No&lt;br /&gt;
|-&lt;br /&gt;
| FriendsService || IFriendsService || No&lt;br /&gt;
|-&lt;br /&gt;
| SimulationService || ISimulationService || No -- remote connector&lt;br /&gt;
|-&lt;br /&gt;
| LibraryService || ILibraryService || No&lt;br /&gt;
|-&lt;br /&gt;
| HGInventoryServicePlugin || IInventoryService || No -- HG suitcase only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
AuthenticationService receives (config, IUserAccountService) as constructor args -- the only service that gets a second argument.&lt;br /&gt;
&lt;br /&gt;
simService and libraryService may be passed directly as arguments (used by standalone mode); if null, the service loads them from config.&lt;br /&gt;
&lt;br /&gt;
Additional config read:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Section !! Key !! Default !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || WelcomeMessage || &amp;quot;Welcome to OpenSim!&amp;quot; || Overridden by MessageUrl if fetch succeeds&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || MessageUrl || (none) || URL to fetch welcome message from at startup&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || RequireInventory || true || Fail login if inventory unavailable&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || AllowRemoteSetLoginLevel || false || Allow SetLevel via HTTP&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || MinLoginLevel || 0 || Minimum UserLevel to permit login&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || GatekeeperURI || (none) || Also checked in [Startup] and [Hypergrid]&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || MapTileURL || (none) ||&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || ProfileServerURL || (none) ||&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || OpenIDServerURL || (none) ||&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || SearchURL || (none) ||&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || Currency || (none) ||&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || ClassifiedFee || (none) ||&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || DestinationGuide || (none) ||&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || AvatarPicker || (none) ||&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || AllowLoginFallbackToAnyRegion || true || Legacy: try any online region if no default/fallback found&lt;br /&gt;
|-&lt;br /&gt;
| [LoginService] || DSTZone || &amp;quot;America/Los_Angeles;Pacific Standard Time&amp;quot; ||&lt;br /&gt;
|-&lt;br /&gt;
| [AccessControl] or [LoginService] || AllowedClients || (none) || Regex matched against viewer string&lt;br /&gt;
|-&lt;br /&gt;
| [AccessControl] or [LoginService] || DeniedClients || (none) || Regex matched against viewer string&lt;br /&gt;
|-&lt;br /&gt;
| [AccessControl] or [LoginService] || DeniedMacs || (none) || Substring-matched against MAC&lt;br /&gt;
|-&lt;br /&gt;
| [AccessControl] or [LoginService] || DeniedID0s || (none) || Substring-matched against id0&lt;br /&gt;
|-&lt;br /&gt;
| [Groups] || MaxAgentGroups || Constants.MaxAgentGroups || Max groups per avatar&lt;br /&gt;
|-&lt;br /&gt;
| [PresenceService] || AllowDuplicatePresences || false || If false, kick existing session on duplicate login&lt;br /&gt;
|-&lt;br /&gt;
| [Messaging] || MessageKey || (none) || Auth key for InstantMessage service&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
WelcomeMessage priority: MessageUrl fetch (if configured and succeeds) overrides WelcomeMessage. If MessageUrl is configured but fetch fails, falls back to WelcomeMessage. &amp;quot;\n&amp;quot; in the message is replaced with a real newline.&lt;br /&gt;
&lt;br /&gt;
SRV_ keys in [LoginService] override service URLs in AgentCircuitData and update the UserAccount record in the database if the value has changed.&lt;br /&gt;
&lt;br /&gt;
Console commands registered once (static Initialized flag):&lt;br /&gt;
* login level &amp;lt;n&amp;gt; -- sets MinLoginLevel&lt;br /&gt;
* login reset -- resets MinLoginLevel to config value&lt;br /&gt;
* login text &amp;lt;text&amp;gt; -- sets welcome message&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Login() ===&lt;br /&gt;
&lt;br /&gt;
 public LoginResponse Login(string firstName, string lastName, string passwd, string startLocation,&lt;br /&gt;
     UUID scopeID, string clientVersion, string channel, string mac, string id0, IPEndPoint clientIP)&lt;br /&gt;
&lt;br /&gt;
The main login path. Returns a LoginResponse (success) or LLFailedLoginResponse (failure). All failures are caught by a top-level try/catch that calls PresenceService.LogoutAgent() and returns LLFailedLoginResponse.InternalError.&lt;br /&gt;
&lt;br /&gt;
Sequence:&lt;br /&gt;
&lt;br /&gt;
# Client checks:&lt;br /&gt;
#* If AllowedClientsRegex is set: clientVersion (or channel + &amp;quot; &amp;quot; + clientVersion) must match -- else LoginBlockedProblem&lt;br /&gt;
#* If DeniedClientsRegex is set: must not match -- else LoginBlockedProblem&lt;br /&gt;
#* If DeniedMacs is set: MAC must not appear as substring -- else LoginBlockedProblem&lt;br /&gt;
#* If DeniedID0s is set: id0 must not appear as substring -- else LoginBlockedProblem&lt;br /&gt;
# Account lookup: GetUserAccount(scopeID, firstName, lastName) -- UserProblem if not found&lt;br /&gt;
# Level check: account.UserLevel &amp;lt; MinLoginLevel -- LoginBlockedProblem&lt;br /&gt;
# God account check: account.PrincipalID == Constants.servicesGodAgentID -- UserProblem (blocked)&lt;br /&gt;
# Scope check: if scopeID provided and account.ScopeID is nonzero and doesn&amp;#039;t match -- UserProblem&lt;br /&gt;
# Authentication:&lt;br /&gt;
#* Strips &amp;quot;$1$&amp;quot; prefix from passwd if present, otherwise MD5-hashes it&lt;br /&gt;
#* Calls AuthenticationService.Authenticate() -- UserProblem if fails&lt;br /&gt;
# Duplicate presence check (if AllowDuplicatePresences = false):&lt;br /&gt;
#* If GridUserInfo shows user is online with a known last region: sends god-kill IM to that region, calls LoggedOut(), returns AlreadyLoggedInProblem&lt;br /&gt;
# Inventory:&lt;br /&gt;
#* If RequireInventory and InventoryService is null: InventoryProblem&lt;br /&gt;
#* Calls HGInventoryService.GetRootFolder() if configured (creates suitcase folder)&lt;br /&gt;
#* Calls InventoryService.GetInventorySkeleton() -- InventoryProblem if empty and RequireInventory&lt;br /&gt;
# Presence login: PresenceService.LoginAgent(principalID, session, secureSession) -- GridProblem if fails&lt;br /&gt;
# Home region lookup via GridService.GetRegionByUUID() using guinfo.HomeRegionID&lt;br /&gt;
# Destination selection: FindDestination() -- GridProblem if returns null; also logs out presence&lt;br /&gt;
# Avatar appearance: AvatarService.GetAppearance() if configured&lt;br /&gt;
# Agent launch: LaunchAgentAtGrid() -- logs out presence and returns failure reason if null&lt;br /&gt;
# GridUserService.LoggedIn() -- called only after successful agent launch&lt;br /&gt;
# Friends list: FriendsService.GetFriends()&lt;br /&gt;
# Active gestures: InventoryService.GetActiveGestures()&lt;br /&gt;
# Builds and returns LLLoginResponse&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== FindDestination() ===&lt;br /&gt;
&lt;br /&gt;
 protected GridRegion FindDestination(...)&lt;br /&gt;
&lt;br /&gt;
Resolves startLocation to a GridRegion. Returns null if no region can be found. Sets out parameters: gatekeeper, where (&amp;quot;home&amp;quot;/&amp;quot;last&amp;quot;/&amp;quot;url&amp;quot;/&amp;quot;safe&amp;quot;), position, lookAt, flags.&lt;br /&gt;
&lt;br /&gt;
startLocation values:&lt;br /&gt;
&lt;br /&gt;
&amp;quot;home&amp;quot;:&lt;br /&gt;
* Uses guinfo.HomeRegionID -- returns home region if found&lt;br /&gt;
* Falls back to GridService.GetDefaultRegions()[0] (where = &amp;quot;safe&amp;quot;)&lt;br /&gt;
* Falls back to FindAlternativeRegion() (where = &amp;quot;safe&amp;quot;)&lt;br /&gt;
* Returns null if nothing found&lt;br /&gt;
&lt;br /&gt;
&amp;quot;last&amp;quot;:&lt;br /&gt;
* Uses guinfo.LastRegionID -- returns last region if found, clamps position within region bounds&lt;br /&gt;
* Falls back to default regions, then FindAlternativeRegion()&lt;br /&gt;
&lt;br /&gt;
URI form (uri:RegionName&amp;amp;x&amp;amp;y&amp;amp;z):&lt;br /&gt;
* Parsed by compiled Regex: ^uri:(?&amp;lt;region&amp;gt;[^&amp;amp;]+)&amp;amp;(?&amp;lt;x&amp;gt;\d+[.]?\d*)&amp;amp;(?&amp;lt;y&amp;gt;\d+[.]?\d*)&amp;amp;(?&amp;lt;z&amp;gt;\d+[.]?\d*)$&lt;br /&gt;
* If region name contains &amp;#039;@&amp;#039;: direct HG login -- explicitly rejected (&amp;quot;no longer supported&amp;quot;), returns null. The original implementation is present but commented out.&lt;br /&gt;
* Otherwise: GridService.GetRegionByName(), falls back to defaults, then FindAlternativeRegion()&lt;br /&gt;
&lt;br /&gt;
FindAlternativeRegion():&lt;br /&gt;
* Tries GetFallbackRegions() near coordinate 1000,1000&lt;br /&gt;
* If AllowLoginFallbackToAnyRegion: tries GetOnlineRegions() near 1000,1000 (up to 10)&lt;br /&gt;
* Returns null if nothing found&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== LaunchAgentAtGrid() ===&lt;br /&gt;
&lt;br /&gt;
 protected AgentCircuitData LaunchAgentAtGrid(...)&lt;br /&gt;
&lt;br /&gt;
Builds AgentCircuitData via MakeAgent() and launches the agent at the destination region.&lt;br /&gt;
&lt;br /&gt;
Two paths depending on whether UserAgentService is configured:&lt;br /&gt;
&lt;br /&gt;
Without UserAgentService (non-HG or standalone):&lt;br /&gt;
* Uses LocalSimulationService if available, else RemoteSimulationService&lt;br /&gt;
* Calls LaunchAgentDirectly(): QueryAccess() then CreateAgent()&lt;br /&gt;
* On failure: tries fallback regions from GridService.GetFallbackRegions()&lt;br /&gt;
&lt;br /&gt;
With UserAgentService (HG-enabled):&lt;br /&gt;
* Constructs gatekeeper GridRegion from m_GatekeeperURL if not already provided&lt;br /&gt;
* Calls LaunchAgentIndirectly(): UserAgentService.LoginAgentToGrid()&lt;br /&gt;
* On failure: tries fallback regions&lt;br /&gt;
&lt;br /&gt;
Returns AgentCircuitData on success, null on failure.&lt;br /&gt;
&lt;br /&gt;
MakeAgent() populates AgentCircuitData:&lt;br /&gt;
* Generates random circuit code&lt;br /&gt;
* Sets appearance (or empty AvatarAppearance if none)&lt;br /&gt;
* Generates random CapsPath&lt;br /&gt;
* Sets child = false (login agent is always root)&lt;br /&gt;
* Calls SetServiceURLs()&lt;br /&gt;
&lt;br /&gt;
SetServiceURLs():&lt;br /&gt;
* Copies ServiceURLs from UserAccount record&lt;br /&gt;
* Overrides with SRV_-prefixed keys from [LoginService] config&lt;br /&gt;
* Appends GatekeeperURI if configured&lt;br /&gt;
* Calls UserAccountService.StoreUserAccount() if any URLs were updated&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== SetLevel() ===&lt;br /&gt;
&lt;br /&gt;
 public Hashtable SetLevel(string firstName, string lastName, string passwd, int level, IPEndPoint clientIP)&lt;br /&gt;
&lt;br /&gt;
Remote login level control. Only active if AllowRemoteSetLoginLevel = true. Requires the caller to authenticate as a user with UserLevel &amp;gt;= 200. Sets m_MinLoginLevel to the requested level. Returns Hashtable with success = &amp;quot;false&amp;quot; on any failure, success = true on success.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== SendAgentGodKillToRegion() ===&lt;br /&gt;
&lt;br /&gt;
Called during duplicate presence handling. Sends a god-kick InstantMessage (dialog = 250) from Constants.servicesGodAgentID to the agent&amp;#039;s last known region via InstantMessageServiceConnector.SendInstantMessage(). Calls GridUserService.LoggedOut() after sending. Returns false if the region cannot be found or has no ServerURI.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Notes ===&lt;br /&gt;
&lt;br /&gt;
* Direct HG login via URI (region@host:port form) is explicitly disabled. The original implementation is present as a large commented-out block with the note that it no longer works due to teleport flag changes and suitcase issues.&lt;br /&gt;
* The god account (Constants.servicesGodAgentID) is explicitly blocked from logging in.&lt;br /&gt;
* GridUserService.LoggedIn() is called only after LaunchAgentAtGrid() succeeds -- if agent launch fails, the GridUser record is not updated.&lt;br /&gt;
* PresenceService.LoginAgent() is called before destination is confirmed. If FindDestination() or LaunchAgentAtGrid() fails, LogoutAgent() is called to clean up.&lt;br /&gt;
* AllowedClients and DeniedClients regex compilation failures are caught and logged; the regex is set to null, meaning the check is skipped rather than blocking all logins.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[OpenSimulator Internals/Code Map/ROBUST]]&lt;br /&gt;
* [[OpenSimulator Internals/Code Map/ROBUST/PresenceService]]&lt;br /&gt;
* [[OpenSimulator Internals/Code Map/ROBUST/AuthenticationService]]&lt;br /&gt;
* [[OpenSimulator Internals/Code Map/ROBUST/GridUserService]]&lt;br /&gt;
* [[OpenSimulator Internals/Connector Architecture]]&lt;/div&gt;</summary>
		<author><name>Jwbshaw</name></author>
	</entry>
</feed>