|| WARNING: If you are seeing this text, then either the Curl RTE has not
|| been installed properly, or you are using a unsupported browser.

|| See the "index.html" file in this directory for more information.






|| This file defines the behavior of the "client" applet.
||
{curl 4.0, 5.0, 5.1, 6.0, 7.0, 8.0 applet}

{applet
    curl-root = "..",
    resync-as-of = {utc-date-time "2009-06-04"}
}

|| Prevent navigation nightmares.
{{get-the-applet}.become-sticky}

{{get-the-applet}.set-title "Puerto Rico, the Online Board Game"}

WARNING: This applet only runs under MSIE or MS Edge in Legacy mode!!!

{big {bold Loading, please wait...}}

If the website is updated, and you are still running an old version of this
applet, you will get an Unexpected Applet Exception telling you to reload.
You may need to hold down shift and/or ctrl to force a complete refresh.

One very common mistake is to download this applet to your local machine,
and to attempt to run it there, instead of browsing it directly from the
website.  This will yield a SyntaxError involving the failure to locate
an instance of the "COM.PHIAL.PUERTO-RICO" package.  If this happens, just
click {link href = {url "http://www.phial.com/puerto-rico/game.curl"}, here}
to browse to the website.

If you are running a firewall that prevents the Curl RTE from accessing the
website, you may get an Unexpected Applet Exception saying something like
"The server name or address could not be resolved.", and perhaps mentioning
a file like "curl-license-5.dat".  Norton Personal Firewall, for example, not
only seems to cause this behavior, but even worse, if you turn it off, it
will turn itself back on whenever it downloads an "update".  At the very
least, you will need to allow the Curl RTE to access the website using
"http" (port 80) and also some port between 21330 and 21339 (it changes
occasionally).

Occasionally, something will cause the website to deliver a corrupt file.
I have added code to attempt to detect and repair such corruption, but some
corruption may not be detectable, and might even cause the corrupt file to
be stored in your internet cache.  If you see any really strange errors,
shut down the Curl RTE, clear your internet cache, and reload this applet.

If all else fails, ask me (Ben Harrison) for help, at
{link href = {url "mailto:benh@phial.com"}, benh@phial.com},
putting "Puerto Rico" in the subject to avoid my spam filters.

{{get-the-applet}.flush}


|| Verify that this is the most up to date copy of the applet, and pull
|| all the source files into the internet cache, verifying their contents.
|| My webserver (or firewall) is somehow serving up empty/corrupted files.
|| HACK: Force loading of the "curl-license" and "curl-access" files.

{let constant corruption:StringArray = {StringArray}}

{let constant timestamp:String = "2009-06-04"}

{define-proc {verify-timestamp}:void
    let current-timestamp:String = ""
    {for i = 0 below 3 do
        {sleep i * 0.01s}
        {with-file-caching-style FileCachingStyle.reload do
            set current-timestamp =
                {{read-from {url "curl-timestamp.txt"}}.trim-right-clone}
        }
        {if current-timestamp == timestamp then {return}}
        let msg:String = "Invalid timestamp (" & current-timestamp & ")!"
        {corruption.append msg}
        {errput msg}
    }
    {error "This applet was released on '", timestamp, "', ",
        "but the webserver was updated on '", current-timestamp, "'.  ",
        "Please reload your applet, holding ctrl and/or shift as appropriate."
    }
}

{verify-timestamp}

{define-proc {verify-aux u:Url, hash:int}:#String
    let a:ByteArray = {read-bytes-from u}
    {if a.size == 0 then
        {return "empty file"}
    }
    let h:int = 0
    {for b in a do
        {if b >= 127 or (b < 32 and b != '\n') then
            {return "unexpected char " & b}
        }
        set h = h * 127 + (b asa int)
    }
    {if h != hash then
        {return "corrupt hash"}
    }
    {return null}
}

{define-proc {verify u:Url, hash:int}:String
    let why:#String = ""
    || Try the cache (ignore errors).
    {with-file-caching-style FileCachingStyle.only-from-cache do
        {try set why = {verify-aux u, hash} catch ex:Exception do}
    }
    {if why == null then {return "."}}
    || Try the web.
    let result:String = "!"
    let msg:String = ""
    {for i = 0 below 5 do
        {sleep i * 0.01s}
        {with-file-caching-style FileCachingStyle.reload do
            set why = {verify-aux u, hash}
        }
        {if why == null then
            {{get-the-applet}.flush}
            {return result}
        }
        set result = result & "*"
        set msg = "Detected corruption of '" & u & "' (" & why & ")!"
        {corruption.append msg}
        {errput msg}
    }
    {error msg}
}

{big {bold Verifying source files}} . . .
{{get-the-applet}.flush}
{verify {url "server.curl"}, 245924062}
{verify {url "game.scurl"}, -1360572103}
{verify {url "util.scurl"}, 1218699779}
{verify {url "procs.scurl"}, -1372501079}
{verify {url "data.scurl"}, 1348873096}
{verify {url "island.scurl"}, 1872858907}
{verify {url "world.scurl"}, 1936384020}
{verify {url "think.scurl"}, -375180282}
{verify {url "lobby.scurl"}, 581187146}
{verify {url "font.scurl"}, 111972134}
{verify {url "views.scurl"}, 622141402}
{verify {url "client.scurl"}, 65246312}
{verify {url "../util/TextPanel.scurl"}, 2140892733}
{big {bold done}}

{big {bold Importing packages...}}
{{get-the-applet}.flush}

{import * from COM.PHIAL.PUERTO-RICO,
    version = "2009.06.04",
    location = "game.scurl"
}

|| Whisper corruption to the server.
{whisper-strings.concat corruption}

|| Become a client.
{become-client}

|| HACK: Detect "local" and "beta" versions of the site.
{let aun:String = {get-the-applet}.url.name}
{let local?:bool = {aun.prefix? "file:"} or {aun.prefix? "curl:"}}
{let beta?:bool = not local? and {aun.find-string "phial.com/"} < 0}

|| Clear the window.
{document-style DefaultDocument}

{if local? then
    {bold color = "red",
        This is a local copy of this applet.  The official version lives
        {link href = {url "http://www.phial.com/puerto-rico/game.curl"}, here}.
    }
 elseif beta? then
    {bold color = "red",
        This is a beta copy of this applet.  The official version lives
        {link href = {url "http://www.phial.com/puerto-rico/game.curl"}, here}.
    }
 else
    null
}

{big Welcome to {bold Puerto Rico}, the Online Board Game.}

{bold WARNING: The official server has been down since 2012, because the
Curl RTE no longer supports linux, and I am only just now getting around
to converting the code to Python, so use the connect/register buttons below
at your own risk.}

Many people have apparently been launching private (or local) servers
using the buttons below for years.

Contact Ben Harrison
({link href = {url "mailto:benh@phial.com"}, benh@phial.com}) with any
comments, suggestions, bug reports, etc.  Please put "Puerto Rico" in
the subject to avoid my spam filters.

New users should probably browse through the
{link href = {url "help.curl"}, target = "_blank", Basic Help}.
All users should probably glance at the
{link href = {url "help.curl#faq"}, target = "_blank",
Frequently Asked Questions}, which is immediately followed by a
list of {link href = {url "help.curl#changes"}, target = "_blank",
Recent Changes as of 2009-06-04}.

{Fill height = 4mm}


It is recommended that you connect to the official
{bold Puerto Rico Server}.

{let username-field:TextField = {TextField width = 30mm}}

{let password-field:PasswordField = {PasswordField width = 30mm}}

{let synchronously:CheckButton =
    {CheckButton
        label = "Connect synchronously (for more detailed errors)"
    }
}

{define-proc {launch local-port:uint16}:void
    let username:String = username-field.value
    let password:String = password-field.value
    {launch-local-server local-port, username, password}
}

{define-proc {connect host:String, port:uint16, register?:bool = false}:void
    {verify-timestamp}
    let username:String = username-field.value
    let password:String = password-field.value
    {if register? and (username == "" or password == "") then
        {error "Please fill in a username and password before registering."}
        ||--{{get-the-applet}.browse-url target = "_blank",
        ||--    {url "help.curl#main-applet"}
        ||--}
 	||--{return}
    }
    let async?:bool = (synchronously.value != true)
    {connect-to-server host, port, username, password,
        async? = async?, register? = register?
    }
}

Enter your username: {value username-field}
and password: {value password-field}

{define-proc {action-normal}:void
    {connect standard-host, standard-port}
}

{let where:String = standard-host & ":" & standard-port}

{username-field.add-event-handler {on Action do {action-normal}}}

{password-field.add-event-handler {on Action do {action-normal}}}

{username-field.request-key-focus}

{CommandButton
    label = "Connect to " & where,
    {on Action do {action-normal}}
}
{Fill width = 10mm}
{value synchronously}

{define-proc {action-register}:void
    {connect standard-host, standard-port, register? = true}
}

{CommandButton
    label = "Register at " & where,
    {on Action do {action-register}}
}
{Fill width = 10mm}
{bold Confused by this applet?  Click
{link href = {url "help.curl#main-applet"}, target = "_blank", here}.}

{Fill height = 4mm}


If you are unable to connect to the Puerto Rico Server, or want to learn
how to play the game and/or use the interface without interacting with
other people, you can launch a private server on your own machine, and
play totally private games against computer opponents.

{CommandButton
    label = "Launch a Private Server on this machine",
    {on Action do {launch 0}}
}

{Fill height = 4mm}


{let port-field:TextField = {TextField width = 2cm}}

{define-proc {action-port}:void
    let port:uint16 = {port-field.value.to-int} asa uint16
    {launch port}
}

{port-field.add-event-handler {on Action do {action-port}}}

{let host-field:TextField = {TextField width = 6cm}}

{define-proc {action-host}:void
    let host:String = host-field.value
    let port:uint16 = {port-field.value.to-int} asa uint16
    {connect host, port}
}

{host-field.add-event-handler {on Action do {action-host}}}

{let advanced-text:any =
    {text
        Advanced users may enter their name (above), and then may either
        enter a port and launch a local server, or enter a port and host and
        connect to a local server which has been launched by somebody else.

        Note that launching or connecting to a local server requires that
        "privilege" has been granted to the directory in which this applet
        (and the "server.curl" applet) lives.  This can be done using the
        Curl RTE Control Panel.

        WARNING: Never grant privilege to a set of applets unless you trust
        them completely, as privileged applets (just like executables) can
        do horrible things to your computer, if they are malicious.

        Enter a port: {value port-field}
        {Fill width = 1cm}
        {CommandButton
            label = "Launch a Local Server on this machine",
            {on Action do {action-port}}
        }

        Enter a host: {value host-field}
        {Fill width = 1cm}
        {CommandButton
            label = "Connect to a Local Server",
            {on Action do {action-host}}
        }

        {CommandButton
            label = "Register with a Local Server (rarely appropriate)",
            {on Action do
                let host:String = host-field.value
                let port:uint16 = {port-field.value.to-int} asa uint16
                {connect host, port, register? = true}
            }
        }

        {Fill height = 5mm}


        You may install a copy of this applet locally for offline use using
        {link href = {url "occ.curl"}, Occasionally Connected Computing}.

        This will allow you to play Puerto Rico while disconnected, offline,
        or behind an aggressive firewall.

        For optimal security, the local server techniques mentioned above
        can be combined with local applet installation, because you can
        actually examine the installed source code to make sure that you
        trust it, and then grant privilege to the local installation
        directory, instead of to the web directory.
    }
}

{CommandButton
    label = "Expose some Extra Features for Advanced Users",
    {on Action at cb:CommandButton do
        {cb.replace-with advanced-text}
    }
}
